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