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