OSDN Git Service

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