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