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