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