Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.content;
     18 
     19 import android.Manifest;
     20 import android.accounts.Account;
     21 import android.app.ActivityManager;
     22 import android.content.ComponentName;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.IContentService;
     26 import android.content.ISyncStatusObserver;
     27 import android.content.PeriodicSync;
     28 import android.content.pm.PackageManager;
     29 import android.content.SyncAdapterType;
     30 import android.content.SyncInfo;
     31 import android.content.SyncRequest;
     32 import android.content.SyncStatusInfo;
     33 import android.database.IContentObserver;
     34 import android.database.sqlite.SQLiteException;
     35 import android.net.Uri;
     36 import android.os.Binder;
     37 import android.os.Bundle;
     38 import android.os.IBinder;
     39 import android.os.Parcel;
     40 import android.os.RemoteException;
     41 import android.os.ServiceManager;
     42 import android.os.SystemProperties;
     43 import android.os.UserHandle;
     44 import android.text.TextUtils;
     45 import android.util.Log;
     46 import android.util.Slog;
     47 import android.util.SparseIntArray;
     48 
     49 import java.io.FileDescriptor;
     50 import java.io.PrintWriter;
     51 import java.security.InvalidParameterException;
     52 import java.util.ArrayList;
     53 import java.util.Collections;
     54 import java.util.Comparator;
     55 import java.util.List;
     56 
     57 /**
     58  * {@hide}
     59  */
     60 public final class ContentService extends IContentService.Stub {
     61     private static final String TAG = "ContentService";
     62     private Context mContext;
     63     private boolean mFactoryTest;
     64     private final ObserverNode mRootNode = new ObserverNode("");
     65     private SyncManager mSyncManager = null;
     66     private final Object mSyncManagerLock = new Object();
     67 
     68     private SyncManager getSyncManager() {
     69         if (SystemProperties.getBoolean("config.disable_network", false)) {
     70             return null;
     71         }
     72 
     73         synchronized(mSyncManagerLock) {
     74             try {
     75                 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
     76                 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
     77             } catch (SQLiteException e) {
     78                 Log.e(TAG, "Can't create SyncManager", e);
     79             }
     80             return mSyncManager;
     81         }
     82     }
     83 
     84     @Override
     85     protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
     86         mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
     87                 "caller doesn't have the DUMP permission");
     88 
     89         // This makes it so that future permission checks will be in the context of this
     90         // process rather than the caller's process. We will restore this before returning.
     91         long identityToken = clearCallingIdentity();
     92         try {
     93             if (mSyncManager == null) {
     94                 pw.println("No SyncManager created!  (Disk full?)");
     95             } else {
     96                 mSyncManager.dump(fd, pw);
     97             }
     98             pw.println();
     99             pw.println("Observer tree:");
    100             synchronized (mRootNode) {
    101                 int[] counts = new int[2];
    102                 final SparseIntArray pidCounts = new SparseIntArray();
    103                 mRootNode.dumpLocked(fd, pw, args, "", "  ", counts, pidCounts);
    104                 pw.println();
    105                 ArrayList<Integer> sorted = new ArrayList<Integer>();
    106                 for (int i=0; i<pidCounts.size(); i++) {
    107                     sorted.add(pidCounts.keyAt(i));
    108                 }
    109                 Collections.sort(sorted, new Comparator<Integer>() {
    110                     @Override
    111                     public int compare(Integer lhs, Integer rhs) {
    112                         int lc = pidCounts.get(lhs);
    113                         int rc = pidCounts.get(rhs);
    114                         if (lc < rc) {
    115                             return 1;
    116                         } else if (lc > rc) {
    117                             return -1;
    118                         }
    119                         return 0;
    120                     }
    121 
    122                 });
    123                 for (int i=0; i<sorted.size(); i++) {
    124                     int pid = sorted.get(i);
    125                     pw.print("  pid "); pw.print(pid); pw.print(": ");
    126                             pw.print(pidCounts.get(pid)); pw.println(" observers");
    127                 }
    128                 pw.println();
    129                 pw.print(" Total number of nodes: "); pw.println(counts[0]);
    130                 pw.print(" Total number of observers: "); pw.println(counts[1]);
    131             }
    132         } finally {
    133             restoreCallingIdentity(identityToken);
    134         }
    135     }
    136 
    137     @Override
    138     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    139             throws RemoteException {
    140         try {
    141             return super.onTransact(code, data, reply, flags);
    142         } catch (RuntimeException e) {
    143             // The content service only throws security exceptions, so let's
    144             // log all others.
    145             if (!(e instanceof SecurityException)) {
    146                 Slog.wtf(TAG, "Content Service Crash", e);
    147             }
    148             throw e;
    149         }
    150     }
    151 
    152     /*package*/ ContentService(Context context, boolean factoryTest) {
    153         mContext = context;
    154         mFactoryTest = factoryTest;
    155     }
    156 
    157     public void systemReady() {
    158         getSyncManager();
    159     }
    160 
    161     /**
    162      * Register a content observer tied to a specific user's view of the provider.
    163      * @param userHandle the user whose view of the provider is to be observed.  May be
    164      *     the calling user without requiring any permission, otherwise the caller needs to
    165      *     hold the INTERACT_ACROSS_USERS_FULL permission.  Pseudousers USER_ALL and
    166      *     USER_CURRENT are properly handled; all other pseudousers are forbidden.
    167      */
    168     @Override
    169     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
    170             IContentObserver observer, int userHandle) {
    171         if (observer == null || uri == null) {
    172             throw new IllegalArgumentException("You must pass a valid uri and observer");
    173         }
    174 
    175         enforceCrossUserPermission(userHandle,
    176                 "no permission to observe other users' provider view");
    177 
    178         if (userHandle < 0) {
    179             if (userHandle == UserHandle.USER_CURRENT) {
    180                 userHandle = ActivityManager.getCurrentUser();
    181             } else if (userHandle != UserHandle.USER_ALL) {
    182                 throw new InvalidParameterException("Bad user handle for registerContentObserver: "
    183                         + userHandle);
    184             }
    185         }
    186 
    187         synchronized (mRootNode) {
    188             mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
    189                     Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
    190             if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
    191                     " with notifyForDescendants " + notifyForDescendants);
    192         }
    193     }
    194 
    195     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
    196             IContentObserver observer) {
    197         registerContentObserver(uri, notifyForDescendants, observer,
    198                 UserHandle.getCallingUserId());
    199     }
    200 
    201     public void unregisterContentObserver(IContentObserver observer) {
    202         if (observer == null) {
    203             throw new IllegalArgumentException("You must pass a valid observer");
    204         }
    205         synchronized (mRootNode) {
    206             mRootNode.removeObserverLocked(observer);
    207             if (false) Log.v(TAG, "Unregistered observer " + observer);
    208         }
    209     }
    210 
    211     /**
    212      * Notify observers of a particular user's view of the provider.
    213      * @param userHandle the user whose view of the provider is to be notified.  May be
    214      *     the calling user without requiring any permission, otherwise the caller needs to
    215      *     hold the INTERACT_ACROSS_USERS_FULL permission.  Pseudousers USER_ALL and
    216      *     USER_CURRENT are properly interpreted; no other pseudousers are allowed.
    217      */
    218     @Override
    219     public void notifyChange(Uri uri, IContentObserver observer,
    220             boolean observerWantsSelfNotifications, boolean syncToNetwork,
    221             int userHandle) {
    222         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    223             Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
    224                     + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
    225         }
    226 
    227         // Notify for any user other than the caller's own requires permission.
    228         final int callingUserHandle = UserHandle.getCallingUserId();
    229         if (userHandle != callingUserHandle) {
    230             mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
    231                     "no permission to notify other users");
    232         }
    233 
    234         // We passed the permission check; resolve pseudouser targets as appropriate
    235         if (userHandle < 0) {
    236             if (userHandle == UserHandle.USER_CURRENT) {
    237                 userHandle = ActivityManager.getCurrentUser();
    238             } else if (userHandle != UserHandle.USER_ALL) {
    239                 throw new InvalidParameterException("Bad user handle for notifyChange: "
    240                         + userHandle);
    241             }
    242         }
    243 
    244         final int uid = Binder.getCallingUid();
    245         // This makes it so that future permission checks will be in the context of this
    246         // process rather than the caller's process. We will restore this before returning.
    247         long identityToken = clearCallingIdentity();
    248         try {
    249             ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
    250             synchronized (mRootNode) {
    251                 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
    252                         userHandle, calls);
    253             }
    254             final int numCalls = calls.size();
    255             for (int i=0; i<numCalls; i++) {
    256                 ObserverCall oc = calls.get(i);
    257                 try {
    258                     oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
    259                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
    260                         Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
    261                     }
    262                 } catch (RemoteException ex) {
    263                     synchronized (mRootNode) {
    264                         Log.w(TAG, "Found dead observer, removing");
    265                         IBinder binder = oc.mObserver.asBinder();
    266                         final ArrayList<ObserverNode.ObserverEntry> list
    267                                 = oc.mNode.mObservers;
    268                         int numList = list.size();
    269                         for (int j=0; j<numList; j++) {
    270                             ObserverNode.ObserverEntry oe = list.get(j);
    271                             if (oe.observer.asBinder() == binder) {
    272                                 list.remove(j);
    273                                 j--;
    274                                 numList--;
    275                             }
    276                         }
    277                     }
    278                 }
    279             }
    280             if (syncToNetwork) {
    281                 SyncManager syncManager = getSyncManager();
    282                 if (syncManager != null) {
    283                     syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
    284                             uri.getAuthority());
    285                 }
    286             }
    287         } finally {
    288             restoreCallingIdentity(identityToken);
    289         }
    290     }
    291 
    292     public void notifyChange(Uri uri, IContentObserver observer,
    293             boolean observerWantsSelfNotifications, boolean syncToNetwork) {
    294         notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
    295                 UserHandle.getCallingUserId());
    296     }
    297 
    298     /**
    299      * Hide this class since it is not part of api,
    300      * but current unittest framework requires it to be public
    301      * @hide
    302      *
    303      */
    304     public static final class ObserverCall {
    305         final ObserverNode mNode;
    306         final IContentObserver mObserver;
    307         final boolean mSelfChange;
    308 
    309         ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
    310             mNode = node;
    311             mObserver = observer;
    312             mSelfChange = selfChange;
    313         }
    314     }
    315 
    316     public void requestSync(Account account, String authority, Bundle extras) {
    317         ContentResolver.validateSyncExtrasBundle(extras);
    318         int userId = UserHandle.getCallingUserId();
    319         int uId = Binder.getCallingUid();
    320 
    321         // This makes it so that future permission checks will be in the context of this
    322         // process rather than the caller's process. We will restore this before returning.
    323         long identityToken = clearCallingIdentity();
    324         try {
    325             SyncManager syncManager = getSyncManager();
    326             if (syncManager != null) {
    327                 syncManager.scheduleSync(account, userId, uId, authority, extras,
    328                         0 /* no delay */, 0 /* no delay */,
    329                         false /* onlyThoseWithUnkownSyncableState */);
    330             }
    331         } finally {
    332             restoreCallingIdentity(identityToken);
    333         }
    334     }
    335 
    336     /**
    337      * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
    338      * either:
    339      *   periodic OR one-off sync.
    340      * and
    341      *   anonymous OR provider sync.
    342      * Depending on the request, we enqueue to suit in the SyncManager.
    343      * @param request The request object. Validation of this object is done by its builder.
    344      */
    345     public void sync(SyncRequest request) {
    346         syncAsUser(request, UserHandle.getCallingUserId());
    347     }
    348 
    349     /**
    350      * If the user id supplied is different to the calling user, the caller must hold the
    351      * INTERACT_ACROSS_USERS_FULL permission.
    352      */
    353     public void syncAsUser(SyncRequest request, int userId) {
    354         enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
    355         int callerUid = Binder.getCallingUid();
    356         // This makes it so that future permission checks will be in the context of this
    357         // process rather than the caller's process. We will restore this before returning.
    358         long identityToken = clearCallingIdentity();
    359         try {
    360             SyncManager syncManager = getSyncManager();
    361             if (syncManager == null) {
    362                 return;
    363             }
    364 
    365             Bundle extras = request.getBundle();
    366             long flextime = request.getSyncFlexTime();
    367             long runAtTime = request.getSyncRunTime();
    368             if (request.isPeriodic()) {
    369                 mContext.enforceCallingOrSelfPermission(
    370                         Manifest.permission.WRITE_SYNC_SETTINGS,
    371                         "no permission to write the sync settings");
    372                 SyncStorageEngine.EndPoint info;
    373                 info = new SyncStorageEngine.EndPoint(
    374                         request.getAccount(), request.getProvider(), userId);
    375                 if (runAtTime < 60) {
    376                     Slog.w(TAG, "Requested poll frequency of " + runAtTime
    377                             + " seconds being rounded up to 60 seconds.");
    378                     runAtTime = 60;
    379                 }
    380                 // Schedule periodic sync.
    381                 getSyncManager().getSyncStorageEngine()
    382                     .updateOrAddPeriodicSync(info, runAtTime, flextime, extras);
    383             } else {
    384                 long beforeRuntimeMillis = (flextime) * 1000;
    385                 long runtimeMillis = runAtTime * 1000;
    386                 syncManager.scheduleSync(
    387                         request.getAccount(), userId, callerUid, request.getProvider(), extras,
    388                         beforeRuntimeMillis, runtimeMillis,
    389                         false /* onlyThoseWithUnknownSyncableState */);
    390             }
    391         } finally {
    392             restoreCallingIdentity(identityToken);
    393         }
    394     }
    395 
    396     /**
    397      * Clear all scheduled sync operations that match the uri and cancel the active sync
    398      * if they match the authority and account, if they are present.
    399      *
    400      * @param account filter the pending and active syncs to cancel using this account, or null.
    401      * @param authority filter the pending and active syncs to cancel using this authority, or
    402      * null.
    403      * @param cname cancel syncs running on this service, or null for provider/account.
    404      */
    405     @Override
    406     public void cancelSync(Account account, String authority, ComponentName cname) {
    407         cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
    408     }
    409 
    410     /**
    411      * Clear all scheduled sync operations that match the uri and cancel the active sync
    412      * if they match the authority and account, if they are present.
    413      *
    414      * <p> If the user id supplied is different to the calling user, the caller must hold the
    415      * INTERACT_ACROSS_USERS_FULL permission.
    416      *
    417      * @param account filter the pending and active syncs to cancel using this account, or null.
    418      * @param authority filter the pending and active syncs to cancel using this authority, or
    419      * null.
    420      * @param userId the user id for which to cancel sync operations.
    421      * @param cname cancel syncs running on this service, or null for provider/account.
    422      */
    423     @Override
    424     public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
    425             int userId) {
    426         if (authority != null && authority.length() == 0) {
    427             throw new IllegalArgumentException("Authority must be non-empty");
    428         }
    429         enforceCrossUserPermission(userId,
    430                 "no permission to modify the sync settings for user " + userId);
    431         // This makes it so that future permission checks will be in the context of this
    432         // process rather than the caller's process. We will restore this before returning.
    433         long identityToken = clearCallingIdentity();
    434         try {
    435             SyncManager syncManager = getSyncManager();
    436             if (syncManager != null) {
    437                 SyncStorageEngine.EndPoint info;
    438                 if (cname == null) {
    439                     info = new SyncStorageEngine.EndPoint(account, authority, userId);
    440                 } else {
    441                     info = new SyncStorageEngine.EndPoint(cname, userId);
    442                 }
    443                 syncManager.clearScheduledSyncOperations(info);
    444                 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
    445             }
    446         } finally {
    447             restoreCallingIdentity(identityToken);
    448         }
    449     }
    450 
    451     public void cancelRequest(SyncRequest request) {
    452         SyncManager syncManager = getSyncManager();
    453         if (syncManager == null) return;
    454         int userId = UserHandle.getCallingUserId();
    455 
    456         long identityToken = clearCallingIdentity();
    457         try {
    458             SyncStorageEngine.EndPoint info;
    459             Bundle extras = new Bundle(request.getBundle());
    460             Account account = request.getAccount();
    461             String provider = request.getProvider();
    462             info = new SyncStorageEngine.EndPoint(account, provider, userId);
    463             if (request.isPeriodic()) {
    464                 // Remove periodic sync.
    465                 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    466                         "no permission to write the sync settings");
    467                 getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras);
    468             }
    469             // Cancel active syncs and clear pending syncs from the queue.
    470             syncManager.cancelScheduledSyncOperation(info, extras);
    471             syncManager.cancelActiveSync(info, extras);
    472         } finally {
    473             restoreCallingIdentity(identityToken);
    474         }
    475     }
    476 
    477     /**
    478      * Get information about the SyncAdapters that are known to the system.
    479      * @return an array of SyncAdapters that have registered with the system
    480      */
    481     @Override
    482     public SyncAdapterType[] getSyncAdapterTypes() {
    483         return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
    484     }
    485 
    486     /**
    487      * Get information about the SyncAdapters that are known to the system for a particular user.
    488      *
    489      * <p> If the user id supplied is different to the calling user, the caller must hold the
    490      * INTERACT_ACROSS_USERS_FULL permission.
    491      *
    492      * @return an array of SyncAdapters that have registered with the system
    493      */
    494     @Override
    495     public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
    496         enforceCrossUserPermission(userId,
    497                 "no permission to read sync settings for user " + userId);
    498         // This makes it so that future permission checks will be in the context of this
    499         // process rather than the caller's process. We will restore this before returning.
    500         final long identityToken = clearCallingIdentity();
    501         try {
    502             SyncManager syncManager = getSyncManager();
    503             return syncManager.getSyncAdapterTypes(userId);
    504         } finally {
    505             restoreCallingIdentity(identityToken);
    506         }
    507     }
    508 
    509     @Override
    510     public boolean getSyncAutomatically(Account account, String providerName) {
    511         return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
    512     }
    513 
    514     /**
    515      * If the user id supplied is different to the calling user, the caller must hold the
    516      * INTERACT_ACROSS_USERS_FULL permission.
    517      */
    518     @Override
    519     public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
    520         enforceCrossUserPermission(userId,
    521                 "no permission to read the sync settings for user " + userId);
    522         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
    523                 "no permission to read the sync settings");
    524 
    525         long identityToken = clearCallingIdentity();
    526         try {
    527             SyncManager syncManager = getSyncManager();
    528             if (syncManager != null) {
    529                 return syncManager.getSyncStorageEngine()
    530                         .getSyncAutomatically(account, userId, providerName);
    531             }
    532         } finally {
    533             restoreCallingIdentity(identityToken);
    534         }
    535         return false;
    536     }
    537 
    538     @Override
    539     public void setSyncAutomatically(Account account, String providerName, boolean sync) {
    540         setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
    541     }
    542 
    543     @Override
    544     public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
    545             int userId) {
    546         if (TextUtils.isEmpty(providerName)) {
    547             throw new IllegalArgumentException("Authority must be non-empty");
    548         }
    549         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    550                 "no permission to write the sync settings");
    551         enforceCrossUserPermission(userId,
    552                 "no permission to modify the sync settings for user " + userId);
    553 
    554         long identityToken = clearCallingIdentity();
    555         try {
    556             SyncManager syncManager = getSyncManager();
    557             if (syncManager != null) {
    558                 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
    559                         providerName, sync);
    560             }
    561         } finally {
    562             restoreCallingIdentity(identityToken);
    563         }
    564     }
    565 
    566     /** Old API. Schedule periodic sync with default flex time. */
    567     @Override
    568     public void addPeriodicSync(Account account, String authority, Bundle extras,
    569             long pollFrequency) {
    570         if (account == null) {
    571             throw new IllegalArgumentException("Account must not be null");
    572         }
    573         if (TextUtils.isEmpty(authority)) {
    574             throw new IllegalArgumentException("Authority must not be empty.");
    575         }
    576         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    577                 "no permission to write the sync settings");
    578 
    579         int userId = UserHandle.getCallingUserId();
    580         if (pollFrequency < 60) {
    581             Slog.w(TAG, "Requested poll frequency of " + pollFrequency
    582                     + " seconds being rounded up to 60 seconds.");
    583             pollFrequency = 60;
    584         }
    585         long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
    586 
    587         long identityToken = clearCallingIdentity();
    588         try {
    589             SyncStorageEngine.EndPoint info =
    590                     new SyncStorageEngine.EndPoint(account, authority, userId);
    591             getSyncManager().getSyncStorageEngine()
    592                 .updateOrAddPeriodicSync(info,
    593                         pollFrequency,
    594                         defaultFlex,
    595                         extras);
    596         } finally {
    597             restoreCallingIdentity(identityToken);
    598         }
    599     }
    600 
    601     public void removePeriodicSync(Account account, String authority, Bundle extras) {
    602         if (account == null) {
    603             throw new IllegalArgumentException("Account must not be null");
    604         }
    605         if (TextUtils.isEmpty(authority)) {
    606             throw new IllegalArgumentException("Authority must not be empty");
    607         }
    608         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    609                 "no permission to write the sync settings");
    610 
    611         int userId = UserHandle.getCallingUserId();
    612         long identityToken = clearCallingIdentity();
    613         try {
    614             getSyncManager().getSyncStorageEngine()
    615                 .removePeriodicSync(
    616                         new SyncStorageEngine.EndPoint(account, authority, userId),
    617                         extras);
    618         } finally {
    619             restoreCallingIdentity(identityToken);
    620         }
    621     }
    622 
    623 
    624     public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
    625             ComponentName cname) {
    626         if (account == null) {
    627             throw new IllegalArgumentException("Account must not be null");
    628         }
    629         if (TextUtils.isEmpty(providerName)) {
    630             throw new IllegalArgumentException("Authority must not be empty");
    631         }
    632         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
    633                 "no permission to read the sync settings");
    634 
    635         int userId = UserHandle.getCallingUserId();
    636         long identityToken = clearCallingIdentity();
    637         try {
    638             return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
    639                     new SyncStorageEngine.EndPoint(account, providerName, userId));
    640         } finally {
    641             restoreCallingIdentity(identityToken);
    642         }
    643     }
    644 
    645     public int getIsSyncable(Account account, String providerName) {
    646         return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
    647     }
    648 
    649     /**
    650      * If the user id supplied is different to the calling user, the caller must hold the
    651      * INTERACT_ACROSS_USERS_FULL permission.
    652      */
    653     public int getIsSyncableAsUser(Account account, String providerName, int userId) {
    654         enforceCrossUserPermission(userId,
    655                 "no permission to read the sync settings for user " + userId);
    656         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
    657                 "no permission to read the sync settings");
    658 
    659         long identityToken = clearCallingIdentity();
    660         try {
    661             SyncManager syncManager = getSyncManager();
    662             if (syncManager != null) {
    663                 return syncManager.getIsSyncable(
    664                         account, userId, providerName);
    665             }
    666         } finally {
    667             restoreCallingIdentity(identityToken);
    668         }
    669         return -1;
    670     }
    671 
    672     public void setIsSyncable(Account account, String providerName, int syncable) {
    673         if (TextUtils.isEmpty(providerName)) {
    674             throw new IllegalArgumentException("Authority must not be empty");
    675         }
    676         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    677                 "no permission to write the sync settings");
    678 
    679         int userId = UserHandle.getCallingUserId();
    680         long identityToken = clearCallingIdentity();
    681         try {
    682             SyncManager syncManager = getSyncManager();
    683             if (syncManager != null) {
    684                 syncManager.getSyncStorageEngine().setIsSyncable(
    685                         account, userId, providerName, syncable);
    686             }
    687         } finally {
    688             restoreCallingIdentity(identityToken);
    689         }
    690     }
    691 
    692     @Override
    693     public boolean getMasterSyncAutomatically() {
    694         return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
    695     }
    696 
    697     /**
    698      * If the user id supplied is different to the calling user, the caller must hold the
    699      * INTERACT_ACROSS_USERS_FULL permission.
    700      */
    701     @Override
    702     public boolean getMasterSyncAutomaticallyAsUser(int userId) {
    703         enforceCrossUserPermission(userId,
    704                 "no permission to read the sync settings for user " + userId);
    705         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
    706                 "no permission to read the sync settings");
    707 
    708         long identityToken = clearCallingIdentity();
    709         try {
    710             SyncManager syncManager = getSyncManager();
    711             if (syncManager != null) {
    712                 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
    713             }
    714         } finally {
    715             restoreCallingIdentity(identityToken);
    716         }
    717         return false;
    718     }
    719 
    720     @Override
    721     public void setMasterSyncAutomatically(boolean flag) {
    722         setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
    723     }
    724 
    725     @Override
    726     public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
    727         enforceCrossUserPermission(userId,
    728                 "no permission to set the sync status for user " + userId);
    729         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
    730                 "no permission to write the sync settings");
    731 
    732         long identityToken = clearCallingIdentity();
    733         try {
    734             SyncManager syncManager = getSyncManager();
    735             if (syncManager != null) {
    736                 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
    737             }
    738         } finally {
    739             restoreCallingIdentity(identityToken);
    740         }
    741     }
    742 
    743     public boolean isSyncActive(Account account, String authority, ComponentName cname) {
    744         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
    745                 "no permission to read the sync stats");
    746         int userId = UserHandle.getCallingUserId();
    747         int callingUid = Binder.getCallingUid();
    748         long identityToken = clearCallingIdentity();
    749         try {
    750             SyncManager syncManager = getSyncManager();
    751             if (syncManager == null) {
    752                 return false;
    753             }
    754             return syncManager.getSyncStorageEngine().isSyncActive(
    755                     new SyncStorageEngine.EndPoint(account, authority, userId));
    756         } finally {
    757             restoreCallingIdentity(identityToken);
    758         }
    759     }
    760 
    761     public List<SyncInfo> getCurrentSyncs() {
    762         return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
    763     }
    764 
    765     /**
    766      * If the user id supplied is different to the calling user, the caller must hold the
    767      * INTERACT_ACROSS_USERS_FULL permission.
    768      */
    769     public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
    770         enforceCrossUserPermission(userId,
    771                 "no permission to read the sync settings for user " + userId);
    772         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
    773                 "no permission to read the sync stats");
    774 
    775         long identityToken = clearCallingIdentity();
    776         try {
    777             return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
    778         } finally {
    779             restoreCallingIdentity(identityToken);
    780         }
    781     }
    782 
    783     public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
    784         return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
    785     }
    786 
    787     /**
    788      * If the user id supplied is different to the calling user, the caller must hold the
    789      * INTERACT_ACROSS_USERS_FULL permission.
    790      */
    791     public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
    792             ComponentName cname, int userId) {
    793         if (TextUtils.isEmpty(authority)) {
    794             throw new IllegalArgumentException("Authority must not be empty");
    795         }
    796 
    797         enforceCrossUserPermission(userId,
    798                 "no permission to read the sync stats for user " + userId);
    799         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
    800                 "no permission to read the sync stats");
    801 
    802         int callerUid = Binder.getCallingUid();
    803         long identityToken = clearCallingIdentity();
    804         try {
    805             SyncManager syncManager = getSyncManager();
    806             if (syncManager == null) {
    807                 return null;
    808             }
    809             SyncStorageEngine.EndPoint info;
    810             if (!(account == null || authority == null)) {
    811                 info = new SyncStorageEngine.EndPoint(account, authority, userId);
    812             } else {
    813                 throw new IllegalArgumentException("Must call sync status with valid authority");
    814             }
    815             return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
    816         } finally {
    817             restoreCallingIdentity(identityToken);
    818         }
    819     }
    820 
    821     public boolean isSyncPending(Account account, String authority, ComponentName cname) {
    822         return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
    823     }
    824 
    825     @Override
    826     public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
    827             int userId) {
    828         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
    829                 "no permission to read the sync stats");
    830         enforceCrossUserPermission(userId,
    831                 "no permission to retrieve the sync settings for user " + userId);
    832         int callerUid = Binder.getCallingUid();
    833         long identityToken = clearCallingIdentity();
    834         SyncManager syncManager = getSyncManager();
    835         if (syncManager == null) return false;
    836 
    837         try {
    838             SyncStorageEngine.EndPoint info;
    839             if (!(account == null || authority == null)) {
    840                 info = new SyncStorageEngine.EndPoint(account, authority, userId);
    841             } else {
    842                 throw new IllegalArgumentException("Invalid authority specified");
    843             }
    844             return syncManager.getSyncStorageEngine().isSyncPending(info);
    845         } finally {
    846             restoreCallingIdentity(identityToken);
    847         }
    848     }
    849 
    850     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
    851         long identityToken = clearCallingIdentity();
    852         try {
    853             SyncManager syncManager = getSyncManager();
    854             if (syncManager != null && callback != null) {
    855                 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
    856             }
    857         } finally {
    858             restoreCallingIdentity(identityToken);
    859         }
    860     }
    861 
    862     public void removeStatusChangeListener(ISyncStatusObserver callback) {
    863         long identityToken = clearCallingIdentity();
    864         try {
    865             SyncManager syncManager = getSyncManager();
    866             if (syncManager != null && callback != null) {
    867                 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
    868             }
    869         } finally {
    870             restoreCallingIdentity(identityToken);
    871         }
    872     }
    873 
    874     public static ContentService main(Context context, boolean factoryTest) {
    875         ContentService service = new ContentService(context, factoryTest);
    876         ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
    877         return service;
    878     }
    879 
    880     /**
    881      * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
    882      * permission, if the userHandle is not for the caller.
    883      *
    884      * @param userHandle the user handle of the user we want to act on behalf of.
    885      * @param message the message to log on security exception.
    886      */
    887     private void enforceCrossUserPermission(int userHandle, String message) {
    888         final int callingUser = UserHandle.getCallingUserId();
    889         if (callingUser != userHandle) {
    890             mContext.enforceCallingOrSelfPermission(
    891                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
    892         }
    893     }
    894 
    895     /**
    896      * Hide this class since it is not part of api,
    897      * but current unittest framework requires it to be public
    898      * @hide
    899      */
    900     public static final class ObserverNode {
    901         private class ObserverEntry implements IBinder.DeathRecipient {
    902             public final IContentObserver observer;
    903             public final int uid;
    904             public final int pid;
    905             public final boolean notifyForDescendants;
    906             private final int userHandle;
    907             private final Object observersLock;
    908 
    909             public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
    910                     int _uid, int _pid, int _userHandle) {
    911                 this.observersLock = observersLock;
    912                 observer = o;
    913                 uid = _uid;
    914                 pid = _pid;
    915                 userHandle = _userHandle;
    916                 notifyForDescendants = n;
    917                 try {
    918                     observer.asBinder().linkToDeath(this, 0);
    919                 } catch (RemoteException e) {
    920                     binderDied();
    921                 }
    922             }
    923 
    924             public void binderDied() {
    925                 synchronized (observersLock) {
    926                     removeObserverLocked(observer);
    927                 }
    928             }
    929 
    930             public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
    931                     String name, String prefix, SparseIntArray pidCounts) {
    932                 pidCounts.put(pid, pidCounts.get(pid)+1);
    933                 pw.print(prefix); pw.print(name); pw.print(": pid=");
    934                         pw.print(pid); pw.print(" uid=");
    935                         pw.print(uid); pw.print(" user=");
    936                         pw.print(userHandle); pw.print(" target=");
    937                         pw.println(Integer.toHexString(System.identityHashCode(
    938                                 observer != null ? observer.asBinder() : null)));
    939             }
    940         }
    941 
    942         public static final int INSERT_TYPE = 0;
    943         public static final int UPDATE_TYPE = 1;
    944         public static final int DELETE_TYPE = 2;
    945 
    946         private String mName;
    947         private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
    948         private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
    949 
    950         public ObserverNode(String name) {
    951             mName = name;
    952         }
    953 
    954         public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
    955                 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
    956             String innerName = null;
    957             if (mObservers.size() > 0) {
    958                 if ("".equals(name)) {
    959                     innerName = mName;
    960                 } else {
    961                     innerName = name + "/" + mName;
    962                 }
    963                 for (int i=0; i<mObservers.size(); i++) {
    964                     counts[1]++;
    965                     mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
    966                             pidCounts);
    967                 }
    968             }
    969             if (mChildren.size() > 0) {
    970                 if (innerName == null) {
    971                     if ("".equals(name)) {
    972                         innerName = mName;
    973                     } else {
    974                         innerName = name + "/" + mName;
    975                     }
    976                 }
    977                 for (int i=0; i<mChildren.size(); i++) {
    978                     counts[0]++;
    979                     mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
    980                             counts, pidCounts);
    981                 }
    982             }
    983         }
    984 
    985         private String getUriSegment(Uri uri, int index) {
    986             if (uri != null) {
    987                 if (index == 0) {
    988                     return uri.getAuthority();
    989                 } else {
    990                     return uri.getPathSegments().get(index - 1);
    991                 }
    992             } else {
    993                 return null;
    994             }
    995         }
    996 
    997         private int countUriSegments(Uri uri) {
    998             if (uri == null) {
    999                 return 0;
   1000             }
   1001             return uri.getPathSegments().size() + 1;
   1002         }
   1003 
   1004         // Invariant:  userHandle is either a hard user number or is USER_ALL
   1005         public void addObserverLocked(Uri uri, IContentObserver observer,
   1006                 boolean notifyForDescendants, Object observersLock,
   1007                 int uid, int pid, int userHandle) {
   1008             addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
   1009                     uid, pid, userHandle);
   1010         }
   1011 
   1012         private void addObserverLocked(Uri uri, int index, IContentObserver observer,
   1013                 boolean notifyForDescendants, Object observersLock,
   1014                 int uid, int pid, int userHandle) {
   1015             // If this is the leaf node add the observer
   1016             if (index == countUriSegments(uri)) {
   1017                 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
   1018                         uid, pid, userHandle));
   1019                 return;
   1020             }
   1021 
   1022             // Look to see if the proper child already exists
   1023             String segment = getUriSegment(uri, index);
   1024             if (segment == null) {
   1025                 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
   1026             }
   1027             int N = mChildren.size();
   1028             for (int i = 0; i < N; i++) {
   1029                 ObserverNode node = mChildren.get(i);
   1030                 if (node.mName.equals(segment)) {
   1031                     node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
   1032                             observersLock, uid, pid, userHandle);
   1033                     return;
   1034                 }
   1035             }
   1036 
   1037             // No child found, create one
   1038             ObserverNode node = new ObserverNode(segment);
   1039             mChildren.add(node);
   1040             node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
   1041                     observersLock, uid, pid, userHandle);
   1042         }
   1043 
   1044         public boolean removeObserverLocked(IContentObserver observer) {
   1045             int size = mChildren.size();
   1046             for (int i = 0; i < size; i++) {
   1047                 boolean empty = mChildren.get(i).removeObserverLocked(observer);
   1048                 if (empty) {
   1049                     mChildren.remove(i);
   1050                     i--;
   1051                     size--;
   1052                 }
   1053             }
   1054 
   1055             IBinder observerBinder = observer.asBinder();
   1056             size = mObservers.size();
   1057             for (int i = 0; i < size; i++) {
   1058                 ObserverEntry entry = mObservers.get(i);
   1059                 if (entry.observer.asBinder() == observerBinder) {
   1060                     mObservers.remove(i);
   1061                     // We no longer need to listen for death notifications. Remove it.
   1062                     observerBinder.unlinkToDeath(entry, 0);
   1063                     break;
   1064                 }
   1065             }
   1066 
   1067             if (mChildren.size() == 0 && mObservers.size() == 0) {
   1068                 return true;
   1069             }
   1070             return false;
   1071         }
   1072 
   1073         private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
   1074                 boolean observerWantsSelfNotifications, int targetUserHandle,
   1075                 ArrayList<ObserverCall> calls) {
   1076             int N = mObservers.size();
   1077             IBinder observerBinder = observer == null ? null : observer.asBinder();
   1078             for (int i = 0; i < N; i++) {
   1079                 ObserverEntry entry = mObservers.get(i);
   1080 
   1081                 // Don't notify the observer if it sent the notification and isn't interested
   1082                 // in self notifications
   1083                 boolean selfChange = (entry.observer.asBinder() == observerBinder);
   1084                 if (selfChange && !observerWantsSelfNotifications) {
   1085                     continue;
   1086                 }
   1087 
   1088                 // Does this observer match the target user?
   1089                 if (targetUserHandle == UserHandle.USER_ALL
   1090                         || entry.userHandle == UserHandle.USER_ALL
   1091                         || targetUserHandle == entry.userHandle) {
   1092                     // Make sure the observer is interested in the notification
   1093                     if (leaf || (!leaf && entry.notifyForDescendants)) {
   1094                         calls.add(new ObserverCall(this, entry.observer, selfChange));
   1095                     }
   1096                 }
   1097             }
   1098         }
   1099 
   1100         /**
   1101          * targetUserHandle is either a hard user handle or is USER_ALL
   1102          */
   1103         public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
   1104                 boolean observerWantsSelfNotifications, int targetUserHandle,
   1105                 ArrayList<ObserverCall> calls) {
   1106             String segment = null;
   1107             int segmentCount = countUriSegments(uri);
   1108             if (index >= segmentCount) {
   1109                 // This is the leaf node, notify all observers
   1110                 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
   1111                         targetUserHandle, calls);
   1112             } else if (index < segmentCount){
   1113                 segment = getUriSegment(uri, index);
   1114                 // Notify any observers at this level who are interested in descendants
   1115                 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
   1116                         targetUserHandle, calls);
   1117             }
   1118 
   1119             int N = mChildren.size();
   1120             for (int i = 0; i < N; i++) {
   1121                 ObserverNode node = mChildren.get(i);
   1122                 if (segment == null || node.mName.equals(segment)) {
   1123                     // We found the child,
   1124                     node.collectObserversLocked(uri, index + 1,
   1125                             observer, observerWantsSelfNotifications, targetUserHandle, calls);
   1126                     if (segment != null) {
   1127                         break;
   1128                     }
   1129                 }
   1130             }
   1131         }
   1132     }
   1133 }
   1134