ContentService.java revision 3b840e378a97a71c6592fb924b39d73d89931a38
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 com.android.server.content;
18
19import android.Manifest;
20import android.accounts.Account;
21import android.annotation.Nullable;
22import android.app.ActivityManager;
23import android.app.ActivityManagerNative;
24import android.app.AppOpsManager;
25import android.app.job.JobInfo;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentProvider;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.IContentService;
32import android.content.ISyncStatusObserver;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.PeriodicSync;
36import android.content.SyncAdapterType;
37import android.content.SyncInfo;
38import android.content.SyncRequest;
39import android.content.SyncStatusInfo;
40import android.content.pm.PackageManager;
41import android.content.pm.PackageManagerInternal;
42import android.content.pm.ProviderInfo;
43import android.database.IContentObserver;
44import android.database.sqlite.SQLiteException;
45import android.net.Uri;
46import android.os.Binder;
47import android.os.Bundle;
48import android.os.FactoryTest;
49import android.os.IBinder;
50import android.os.Parcel;
51import android.os.RemoteException;
52import android.os.SystemProperties;
53import android.os.UserHandle;
54import android.text.TextUtils;
55import android.util.ArrayMap;
56import android.util.Log;
57import android.util.Pair;
58import android.util.Slog;
59import android.util.SparseArray;
60import android.util.SparseIntArray;
61
62import com.android.internal.annotations.GuardedBy;
63import com.android.internal.util.IndentingPrintWriter;
64import com.android.server.LocalServices;
65import com.android.server.SystemService;
66
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
69import java.security.InvalidParameterException;
70import java.util.ArrayList;
71import java.util.Collections;
72import java.util.Comparator;
73import java.util.List;
74
75/**
76 * {@hide}
77 */
78public final class ContentService extends IContentService.Stub {
79    static final String TAG = "ContentService";
80    static final boolean DEBUG = false;
81
82    public static class Lifecycle extends SystemService {
83        private ContentService mService;
84
85        public Lifecycle(Context context) {
86            super(context);
87        }
88
89        @Override
90        public void onStart() {
91            final boolean factoryTest = (FactoryTest
92                    .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
93            mService = new ContentService(getContext(), factoryTest);
94            publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
95        }
96
97        @Override
98        public void onBootPhase(int phase) {
99            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
100                mService.systemReady();
101            }
102        }
103
104        @Override
105        public void onCleanupUser(int userHandle) {
106            synchronized (mService.mCache) {
107                mService.mCache.remove(userHandle);
108            }
109        }
110    }
111
112    private Context mContext;
113    private boolean mFactoryTest;
114
115    private final ObserverNode mRootNode = new ObserverNode("");
116
117    private SyncManager mSyncManager = null;
118    private final Object mSyncManagerLock = new Object();
119
120    /**
121     * Map from userId to providerPackageName to [clientPackageName, uri] to
122     * value. This structure is carefully optimized to keep invalidation logic
123     * as cheap as possible.
124     */
125    @GuardedBy("mCache")
126    private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
127            mCache = new SparseArray<>();
128
129    private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
130        @Override
131        public void onReceive(Context context, Intent intent) {
132            synchronized (mCache) {
133                if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
134                    mCache.clear();
135                } else {
136                    final Uri data = intent.getData();
137                    if (data != null) {
138                        final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
139                                UserHandle.USER_NULL);
140                        final String packageName = data.getSchemeSpecificPart();
141                        invalidateCacheLocked(userId, packageName, null);
142                    }
143                }
144            }
145        }
146    };
147
148    private SyncManager getSyncManager() {
149        if (SystemProperties.getBoolean("config.disable_network", false)) {
150            return null;
151        }
152
153        synchronized(mSyncManagerLock) {
154            try {
155                // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
156                if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
157            } catch (SQLiteException e) {
158                Log.e(TAG, "Can't create SyncManager", e);
159            }
160            return mSyncManager;
161        }
162    }
163
164    @Override
165    protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
166        mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
167                "caller doesn't have the DUMP permission");
168
169        final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, "  ");
170
171        // This makes it so that future permission checks will be in the context of this
172        // process rather than the caller's process. We will restore this before returning.
173        final long identityToken = clearCallingIdentity();
174        try {
175            if (mSyncManager == null) {
176                pw.println("No SyncManager created!  (Disk full?)");
177            } else {
178                mSyncManager.dump(fd, pw);
179            }
180            pw.println();
181            pw.println("Observer tree:");
182            synchronized (mRootNode) {
183                int[] counts = new int[2];
184                final SparseIntArray pidCounts = new SparseIntArray();
185                mRootNode.dumpLocked(fd, pw, args, "", "  ", counts, pidCounts);
186                pw.println();
187                ArrayList<Integer> sorted = new ArrayList<Integer>();
188                for (int i=0; i<pidCounts.size(); i++) {
189                    sorted.add(pidCounts.keyAt(i));
190                }
191                Collections.sort(sorted, new Comparator<Integer>() {
192                    @Override
193                    public int compare(Integer lhs, Integer rhs) {
194                        int lc = pidCounts.get(lhs);
195                        int rc = pidCounts.get(rhs);
196                        if (lc < rc) {
197                            return 1;
198                        } else if (lc > rc) {
199                            return -1;
200                        }
201                        return 0;
202                    }
203
204                });
205                for (int i=0; i<sorted.size(); i++) {
206                    int pid = sorted.get(i);
207                    pw.print("  pid "); pw.print(pid); pw.print(": ");
208                    pw.print(pidCounts.get(pid)); pw.println(" observers");
209                }
210                pw.println();
211                pw.print(" Total number of nodes: "); pw.println(counts[0]);
212                pw.print(" Total number of observers: "); pw.println(counts[1]);
213            }
214
215            synchronized (mCache) {
216                pw.println();
217                pw.println("Cached content:");
218                pw.increaseIndent();
219                for (int i = 0; i < mCache.size(); i++) {
220                    pw.println("User " + mCache.keyAt(i) + ":");
221                    pw.increaseIndent();
222                    pw.println(mCache.valueAt(i));
223                    pw.decreaseIndent();
224                }
225                pw.decreaseIndent();
226            }
227        } finally {
228            restoreCallingIdentity(identityToken);
229        }
230    }
231
232    @Override
233    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
234            throws RemoteException {
235        try {
236            return super.onTransact(code, data, reply, flags);
237        } catch (RuntimeException e) {
238            // The content service only throws security exceptions, so let's
239            // log all others.
240            if (!(e instanceof SecurityException)) {
241                Slog.wtf(TAG, "Content Service Crash", e);
242            }
243            throw e;
244        }
245    }
246
247    /*package*/ ContentService(Context context, boolean factoryTest) {
248        mContext = context;
249        mFactoryTest = factoryTest;
250
251        // Let the package manager query for the sync adapters for a given authority
252        // as we grant default permissions to sync adapters for specific authorities.
253        PackageManagerInternal packageManagerInternal = LocalServices.getService(
254                PackageManagerInternal.class);
255        packageManagerInternal.setSyncAdapterPackagesprovider(
256                new PackageManagerInternal.SyncAdapterPackagesProvider() {
257                    @Override
258                    public String[] getPackages(String authority, int userId) {
259                        return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
260                    }
261                });
262
263        final IntentFilter packageFilter = new IntentFilter();
264        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
265        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
266        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
267        packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
268        packageFilter.addDataScheme("package");
269        mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
270                packageFilter, null, null);
271
272        final IntentFilter localeFilter = new IntentFilter();
273        localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
274        mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
275                localeFilter, null, null);
276    }
277
278    void systemReady() {
279        getSyncManager();
280    }
281
282    /**
283     * Register a content observer tied to a specific user's view of the provider.
284     * @param userHandle the user whose view of the provider is to be observed.  May be
285     *     the calling user without requiring any permission, otherwise the caller needs to
286     *     hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
287     *     Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
288     *     are forbidden.
289     */
290    @Override
291    public void registerContentObserver(Uri uri, boolean notifyForDescendants,
292                                        IContentObserver observer, int userHandle) {
293        if (observer == null || uri == null) {
294            throw new IllegalArgumentException("You must pass a valid uri and observer");
295        }
296
297        final int uid = Binder.getCallingUid();
298        final int pid = Binder.getCallingPid();
299        final int callingUserHandle = UserHandle.getCallingUserId();
300        // Registering an observer for any user other than the calling user requires uri grant or
301        // cross user permission
302        if (callingUserHandle != userHandle) {
303            if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle)
304                    != PackageManager.PERMISSION_GRANTED) {
305                enforceCrossUserPermission(userHandle,
306                        "no permission to observe other users' provider view");
307            }
308        }
309
310        if (userHandle < 0) {
311            if (userHandle == UserHandle.USER_CURRENT) {
312                userHandle = ActivityManager.getCurrentUser();
313            } else if (userHandle != UserHandle.USER_ALL) {
314                throw new InvalidParameterException("Bad user handle for registerContentObserver: "
315                        + userHandle);
316            }
317        }
318
319        synchronized (mRootNode) {
320            mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
321                    uid, pid, userHandle);
322            if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
323                    " with notifyForDescendants " + notifyForDescendants);
324        }
325    }
326
327    public void registerContentObserver(Uri uri, boolean notifyForDescendants,
328                                        IContentObserver observer) {
329        registerContentObserver(uri, notifyForDescendants, observer,
330                UserHandle.getCallingUserId());
331    }
332
333    @Override
334    public void unregisterContentObserver(IContentObserver observer) {
335        if (observer == null) {
336            throw new IllegalArgumentException("You must pass a valid observer");
337        }
338        synchronized (mRootNode) {
339            mRootNode.removeObserverLocked(observer);
340            if (false) Log.v(TAG, "Unregistered observer " + observer);
341        }
342    }
343
344    /**
345     * Notify observers of a particular user's view of the provider.
346     * @param userHandle the user whose view of the provider is to be notified.  May be
347     *     the calling user without requiring any permission, otherwise the caller needs to
348     *     hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
349     *     Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
350     *     allowed.
351     */
352    @Override
353    public void notifyChange(Uri uri, IContentObserver observer,
354                             boolean observerWantsSelfNotifications, int flags,
355                             int userHandle) {
356        if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
357                + " from observer " + observer + ", flags " + Integer.toHexString(flags));
358
359        if (uri == null) {
360            throw new NullPointerException("Uri must not be null");
361        }
362
363        final int uid = Binder.getCallingUid();
364        final int pid = Binder.getCallingPid();
365        final int callingUserHandle = UserHandle.getCallingUserId();
366        // Notify for any user other than the caller requires uri grant or cross user permission
367        if (callingUserHandle != userHandle) {
368            if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
369                    userHandle) != PackageManager.PERMISSION_GRANTED) {
370                enforceCrossUserPermission(userHandle, "no permission to notify other users");
371            }
372        }
373
374        // We passed the permission check; resolve pseudouser targets as appropriate
375        if (userHandle < 0) {
376            if (userHandle == UserHandle.USER_CURRENT) {
377                userHandle = ActivityManager.getCurrentUser();
378            } else if (userHandle != UserHandle.USER_ALL) {
379                throw new InvalidParameterException("Bad user handle for notifyChange: "
380                        + userHandle);
381            }
382        }
383
384        // This makes it so that future permission checks will be in the context of this
385        // process rather than the caller's process. We will restore this before returning.
386        long identityToken = clearCallingIdentity();
387        try {
388            ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
389            synchronized (mRootNode) {
390                mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
391                        flags, userHandle, calls);
392            }
393            final int numCalls = calls.size();
394            for (int i=0; i<numCalls; i++) {
395                ObserverCall oc = calls.get(i);
396                try {
397                    // If the uri does not belong to the same user as the observer: we must add
398                    // the userId to the uri. Otherewise the observer would think the uri belongs
399                    // to his user.
400                    final Uri tempUri;
401                    if (oc.mObserverUserId != userHandle) {
402                        tempUri = ContentProvider.maybeAddUserId(uri, userHandle);
403                    } else {
404                        tempUri = uri;
405                    }
406                    oc.mObserver.onChange(oc.mSelfChange, tempUri, userHandle);
407                    if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
408                            + tempUri);
409                } catch (RemoteException ex) {
410                    synchronized (mRootNode) {
411                        Log.w(TAG, "Found dead observer, removing");
412                        IBinder binder = oc.mObserver.asBinder();
413                        final ArrayList<ObserverNode.ObserverEntry> list
414                                = oc.mNode.mObservers;
415                        int numList = list.size();
416                        for (int j=0; j<numList; j++) {
417                            ObserverNode.ObserverEntry oe = list.get(j);
418                            if (oe.observer.asBinder() == binder) {
419                                list.remove(j);
420                                j--;
421                                numList--;
422                            }
423                        }
424                    }
425                }
426            }
427            if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
428                SyncManager syncManager = getSyncManager();
429                if (syncManager != null) {
430                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
431                            uri.getAuthority());
432                }
433            }
434
435            synchronized (mCache) {
436                final String providerPackageName = getProviderPackageName(uri);
437                invalidateCacheLocked(userHandle, providerPackageName, uri);
438            }
439        } finally {
440            restoreCallingIdentity(identityToken);
441        }
442    }
443
444    private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
445        try {
446            return ActivityManagerNative.getDefault().checkUriPermission(
447                    uri, pid, uid, modeFlags, userHandle, null);
448        } catch (RemoteException e) {
449            return PackageManager.PERMISSION_DENIED;
450        }
451    }
452
453    public void notifyChange(Uri uri, IContentObserver observer,
454                             boolean observerWantsSelfNotifications, boolean syncToNetwork) {
455        notifyChange(uri, observer, observerWantsSelfNotifications,
456                syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
457                UserHandle.getCallingUserId());
458    }
459
460    /**
461     * Hide this class since it is not part of api,
462     * but current unittest framework requires it to be public
463     * @hide
464     *
465     */
466    public static final class ObserverCall {
467        final ObserverNode mNode;
468        final IContentObserver mObserver;
469        final boolean mSelfChange;
470        final int mObserverUserId;
471
472        ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange, int observerUserId) {
473            mNode = node;
474            mObserver = observer;
475            mSelfChange = selfChange;
476            mObserverUserId = observerUserId;
477        }
478    }
479
480    @Override
481    public void requestSync(Account account, String authority, Bundle extras) {
482        Bundle.setDefusable(extras, true);
483        ContentResolver.validateSyncExtrasBundle(extras);
484        int userId = UserHandle.getCallingUserId();
485        int uId = Binder.getCallingUid();
486
487        // This makes it so that future permission checks will be in the context of this
488        // process rather than the caller's process. We will restore this before returning.
489        long identityToken = clearCallingIdentity();
490        try {
491            SyncManager syncManager = getSyncManager();
492            if (syncManager != null) {
493                syncManager.scheduleSync(account, userId, uId, authority, extras,
494                        0 /* no delay */, 0 /* no delay */,
495                        false /* onlyThoseWithUnkownSyncableState */);
496            }
497        } finally {
498            restoreCallingIdentity(identityToken);
499        }
500    }
501
502    /**
503     * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
504     * either:
505     *   periodic OR one-off sync.
506     * and
507     *   anonymous OR provider sync.
508     * Depending on the request, we enqueue to suit in the SyncManager.
509     * @param request The request object. Validation of this object is done by its builder.
510     */
511    @Override
512    public void sync(SyncRequest request) {
513        syncAsUser(request, UserHandle.getCallingUserId());
514    }
515
516    private long clampPeriod(long period) {
517        long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
518        if (period < minPeriod) {
519            Slog.w(TAG, "Requested poll frequency of " + period
520                    + " seconds being rounded up to " + minPeriod + "s.");
521            period = minPeriod;
522        }
523        return period;
524    }
525
526    /**
527     * If the user id supplied is different to the calling user, the caller must hold the
528     * INTERACT_ACROSS_USERS_FULL permission.
529     */
530    @Override
531    public void syncAsUser(SyncRequest request, int userId) {
532        enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
533        int callerUid = Binder.getCallingUid();
534        // This makes it so that future permission checks will be in the context of this
535        // process rather than the caller's process. We will restore this before returning.
536        long identityToken = clearCallingIdentity();
537        try {
538            SyncManager syncManager = getSyncManager();
539            if (syncManager == null) {
540                return;
541            }
542
543            Bundle extras = request.getBundle();
544            long flextime = request.getSyncFlexTime();
545            long runAtTime = request.getSyncRunTime();
546            if (request.isPeriodic()) {
547                mContext.enforceCallingOrSelfPermission(
548                        Manifest.permission.WRITE_SYNC_SETTINGS,
549                        "no permission to write the sync settings");
550                SyncStorageEngine.EndPoint info;
551                info = new SyncStorageEngine.EndPoint(
552                        request.getAccount(), request.getProvider(), userId);
553
554                runAtTime = clampPeriod(runAtTime);
555                // Schedule periodic sync.
556                getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
557                        flextime, extras);
558            } else {
559                long beforeRuntimeMillis = (flextime) * 1000;
560                long runtimeMillis = runAtTime * 1000;
561                syncManager.scheduleSync(
562                        request.getAccount(), userId, callerUid, request.getProvider(), extras,
563                        beforeRuntimeMillis, runtimeMillis,
564                        false /* onlyThoseWithUnknownSyncableState */);
565            }
566        } finally {
567            restoreCallingIdentity(identityToken);
568        }
569    }
570
571    /**
572     * Clear all scheduled sync operations that match the uri and cancel the active sync
573     * if they match the authority and account, if they are present.
574     *
575     * @param account filter the pending and active syncs to cancel using this account, or null.
576     * @param authority filter the pending and active syncs to cancel using this authority, or
577     * null.
578     * @param cname cancel syncs running on this service, or null for provider/account.
579     */
580    @Override
581    public void cancelSync(Account account, String authority, ComponentName cname) {
582        cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
583    }
584
585    /**
586     * Clear all scheduled sync operations that match the uri and cancel the active sync
587     * if they match the authority and account, if they are present.
588     *
589     * <p> If the user id supplied is different to the calling user, the caller must hold the
590     * INTERACT_ACROSS_USERS_FULL permission.
591     *
592     * @param account filter the pending and active syncs to cancel using this account, or null.
593     * @param authority filter the pending and active syncs to cancel using this authority, or
594     * null.
595     * @param userId the user id for which to cancel sync operations.
596     * @param cname cancel syncs running on this service, or null for provider/account.
597     */
598    @Override
599    public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
600                                 int userId) {
601        if (authority != null && authority.length() == 0) {
602            throw new IllegalArgumentException("Authority must be non-empty");
603        }
604        enforceCrossUserPermission(userId,
605                "no permission to modify the sync settings for user " + userId);
606        // This makes it so that future permission checks will be in the context of this
607        // process rather than the caller's process. We will restore this before returning.
608        long identityToken = clearCallingIdentity();
609        if (cname != null) {
610            Slog.e(TAG, "cname not null.");
611            return;
612        }
613        try {
614            SyncManager syncManager = getSyncManager();
615            if (syncManager != null) {
616                SyncStorageEngine.EndPoint info;
617                info = new SyncStorageEngine.EndPoint(account, authority, userId);
618                syncManager.clearScheduledSyncOperations(info);
619                syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
620            }
621        } finally {
622            restoreCallingIdentity(identityToken);
623        }
624    }
625
626    @Override
627    public void cancelRequest(SyncRequest request) {
628        SyncManager syncManager = getSyncManager();
629        if (syncManager == null) return;
630        int userId = UserHandle.getCallingUserId();
631
632        long identityToken = clearCallingIdentity();
633        try {
634            SyncStorageEngine.EndPoint info;
635            Bundle extras = new Bundle(request.getBundle());
636            Account account = request.getAccount();
637            String provider = request.getProvider();
638            info = new SyncStorageEngine.EndPoint(account, provider, userId);
639            if (request.isPeriodic()) {
640                // Remove periodic sync.
641                mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
642                        "no permission to write the sync settings");
643                getSyncManager().removePeriodicSync(info, extras);
644            }
645            // Cancel active syncs and clear pending syncs from the queue.
646            syncManager.cancelScheduledSyncOperation(info, extras);
647            syncManager.cancelActiveSync(info, extras);
648        } finally {
649            restoreCallingIdentity(identityToken);
650        }
651    }
652
653    /**
654     * Get information about the SyncAdapters that are known to the system.
655     * @return an array of SyncAdapters that have registered with the system
656     */
657    @Override
658    public SyncAdapterType[] getSyncAdapterTypes() {
659        return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
660    }
661
662    /**
663     * Get information about the SyncAdapters that are known to the system for a particular user.
664     *
665     * <p> If the user id supplied is different to the calling user, the caller must hold the
666     * INTERACT_ACROSS_USERS_FULL permission.
667     *
668     * @return an array of SyncAdapters that have registered with the system
669     */
670    @Override
671    public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
672        enforceCrossUserPermission(userId,
673                "no permission to read sync settings for user " + userId);
674        // This makes it so that future permission checks will be in the context of this
675        // process rather than the caller's process. We will restore this before returning.
676        final long identityToken = clearCallingIdentity();
677        try {
678            SyncManager syncManager = getSyncManager();
679            return syncManager.getSyncAdapterTypes(userId);
680        } finally {
681            restoreCallingIdentity(identityToken);
682        }
683    }
684
685    @Override
686    public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
687        enforceCrossUserPermission(userId,
688                "no permission to read sync settings for user " + userId);
689        // This makes it so that future permission checks will be in the context of this
690        // process rather than the caller's process. We will restore this before returning.
691        final long identityToken = clearCallingIdentity();
692        try {
693            SyncManager syncManager = getSyncManager();
694            return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
695        } finally {
696            restoreCallingIdentity(identityToken);
697        }
698    }
699
700    @Override
701    public boolean getSyncAutomatically(Account account, String providerName) {
702        return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
703    }
704
705    /**
706     * If the user id supplied is different to the calling user, the caller must hold the
707     * INTERACT_ACROSS_USERS_FULL permission.
708     */
709    @Override
710    public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
711        enforceCrossUserPermission(userId,
712                "no permission to read the sync settings for user " + userId);
713        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
714                "no permission to read the sync settings");
715
716        long identityToken = clearCallingIdentity();
717        try {
718            SyncManager syncManager = getSyncManager();
719            if (syncManager != null) {
720                return syncManager.getSyncStorageEngine()
721                        .getSyncAutomatically(account, userId, providerName);
722            }
723        } finally {
724            restoreCallingIdentity(identityToken);
725        }
726        return false;
727    }
728
729    @Override
730    public void setSyncAutomatically(Account account, String providerName, boolean sync) {
731        setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
732    }
733
734    @Override
735    public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
736                                           int userId) {
737        if (TextUtils.isEmpty(providerName)) {
738            throw new IllegalArgumentException("Authority must be non-empty");
739        }
740        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
741                "no permission to write the sync settings");
742        enforceCrossUserPermission(userId,
743                "no permission to modify the sync settings for user " + userId);
744
745        long identityToken = clearCallingIdentity();
746        try {
747            SyncManager syncManager = getSyncManager();
748            if (syncManager != null) {
749                syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
750                        providerName, sync);
751            }
752        } finally {
753            restoreCallingIdentity(identityToken);
754        }
755    }
756
757    /** Old API. Schedule periodic sync with default flexMillis time. */
758    @Override
759    public void addPeriodicSync(Account account, String authority, Bundle extras,
760                                long pollFrequency) {
761        Bundle.setDefusable(extras, true);
762        if (account == null) {
763            throw new IllegalArgumentException("Account must not be null");
764        }
765        if (TextUtils.isEmpty(authority)) {
766            throw new IllegalArgumentException("Authority must not be empty.");
767        }
768        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
769                "no permission to write the sync settings");
770
771        int userId = UserHandle.getCallingUserId();
772
773        pollFrequency = clampPeriod(pollFrequency);
774        long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
775
776        long identityToken = clearCallingIdentity();
777        try {
778            SyncStorageEngine.EndPoint info =
779                    new SyncStorageEngine.EndPoint(account, authority, userId);
780            getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
781                    defaultFlex, extras);
782        } finally {
783            restoreCallingIdentity(identityToken);
784        }
785    }
786
787    @Override
788    public void removePeriodicSync(Account account, String authority, Bundle extras) {
789        Bundle.setDefusable(extras, true);
790        if (account == null) {
791            throw new IllegalArgumentException("Account must not be null");
792        }
793        if (TextUtils.isEmpty(authority)) {
794            throw new IllegalArgumentException("Authority must not be empty");
795        }
796        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
797                "no permission to write the sync settings");
798
799        int userId = UserHandle.getCallingUserId();
800        long identityToken = clearCallingIdentity();
801        try {
802            getSyncManager()
803                    .removePeriodicSync(
804                            new SyncStorageEngine.EndPoint(account, authority, userId),
805                            extras);
806        } finally {
807            restoreCallingIdentity(identityToken);
808        }
809    }
810
811    @Override
812    public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
813                                               ComponentName cname) {
814        if (account == null) {
815            throw new IllegalArgumentException("Account must not be null");
816        }
817        if (TextUtils.isEmpty(providerName)) {
818            throw new IllegalArgumentException("Authority must not be empty");
819        }
820        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
821                "no permission to read the sync settings");
822
823        int userId = UserHandle.getCallingUserId();
824        long identityToken = clearCallingIdentity();
825        try {
826            return getSyncManager().getPeriodicSyncs(
827                    new SyncStorageEngine.EndPoint(account, providerName, userId));
828        } finally {
829            restoreCallingIdentity(identityToken);
830        }
831    }
832
833    @Override
834    public int getIsSyncable(Account account, String providerName) {
835        return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
836    }
837
838    /**
839     * If the user id supplied is different to the calling user, the caller must hold the
840     * INTERACT_ACROSS_USERS_FULL permission.
841     */
842    @Override
843    public int getIsSyncableAsUser(Account account, String providerName, int userId) {
844        enforceCrossUserPermission(userId,
845                "no permission to read the sync settings for user " + userId);
846        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
847                "no permission to read the sync settings");
848
849        long identityToken = clearCallingIdentity();
850        try {
851            SyncManager syncManager = getSyncManager();
852            if (syncManager != null) {
853                return syncManager.getIsSyncable(
854                        account, userId, providerName);
855            }
856        } finally {
857            restoreCallingIdentity(identityToken);
858        }
859        return -1;
860    }
861
862    @Override
863    public void setIsSyncable(Account account, String providerName, int syncable) {
864        if (TextUtils.isEmpty(providerName)) {
865            throw new IllegalArgumentException("Authority must not be empty");
866        }
867        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
868                "no permission to write the sync settings");
869
870        int userId = UserHandle.getCallingUserId();
871        long identityToken = clearCallingIdentity();
872        try {
873            SyncManager syncManager = getSyncManager();
874            if (syncManager != null) {
875                syncManager.getSyncStorageEngine().setIsSyncable(
876                        account, userId, providerName, syncable);
877            }
878        } finally {
879            restoreCallingIdentity(identityToken);
880        }
881    }
882
883    @Override
884    public boolean getMasterSyncAutomatically() {
885        return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
886    }
887
888    /**
889     * If the user id supplied is different to the calling user, the caller must hold the
890     * INTERACT_ACROSS_USERS_FULL permission.
891     */
892    @Override
893    public boolean getMasterSyncAutomaticallyAsUser(int userId) {
894        enforceCrossUserPermission(userId,
895                "no permission to read the sync settings for user " + userId);
896        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
897                "no permission to read the sync settings");
898
899        long identityToken = clearCallingIdentity();
900        try {
901            SyncManager syncManager = getSyncManager();
902            if (syncManager != null) {
903                return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
904            }
905        } finally {
906            restoreCallingIdentity(identityToken);
907        }
908        return false;
909    }
910
911    @Override
912    public void setMasterSyncAutomatically(boolean flag) {
913        setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
914    }
915
916    @Override
917    public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
918        enforceCrossUserPermission(userId,
919                "no permission to set the sync status for user " + userId);
920        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
921                "no permission to write the sync settings");
922
923        long identityToken = clearCallingIdentity();
924        try {
925            SyncManager syncManager = getSyncManager();
926            if (syncManager != null) {
927                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
928            }
929        } finally {
930            restoreCallingIdentity(identityToken);
931        }
932    }
933
934    @Override
935    public boolean isSyncActive(Account account, String authority, ComponentName cname) {
936        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
937                "no permission to read the sync stats");
938        int userId = UserHandle.getCallingUserId();
939        long identityToken = clearCallingIdentity();
940        try {
941            SyncManager syncManager = getSyncManager();
942            if (syncManager == null) {
943                return false;
944            }
945            return syncManager.getSyncStorageEngine().isSyncActive(
946                    new SyncStorageEngine.EndPoint(account, authority, userId));
947        } finally {
948            restoreCallingIdentity(identityToken);
949        }
950    }
951
952    @Override
953    public List<SyncInfo> getCurrentSyncs() {
954        return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
955    }
956
957    /**
958     * If the user id supplied is different to the calling user, the caller must hold the
959     * INTERACT_ACROSS_USERS_FULL permission.
960     */
961    @Override
962    public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
963        enforceCrossUserPermission(userId,
964                "no permission to read the sync settings for user " + userId);
965        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
966                "no permission to read the sync stats");
967
968        final boolean canAccessAccounts =
969            mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
970                == PackageManager.PERMISSION_GRANTED;
971        long identityToken = clearCallingIdentity();
972        try {
973            return getSyncManager().getSyncStorageEngine()
974                .getCurrentSyncsCopy(userId, canAccessAccounts);
975        } finally {
976            restoreCallingIdentity(identityToken);
977        }
978    }
979
980    @Override
981    public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
982        return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
983    }
984
985    /**
986     * If the user id supplied is different to the calling user, the caller must hold the
987     * INTERACT_ACROSS_USERS_FULL permission.
988     */
989    @Override
990    public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
991                                              ComponentName cname, int userId) {
992        if (TextUtils.isEmpty(authority)) {
993            throw new IllegalArgumentException("Authority must not be empty");
994        }
995
996        enforceCrossUserPermission(userId,
997                "no permission to read the sync stats for user " + userId);
998        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
999                "no permission to read the sync stats");
1000
1001        long identityToken = clearCallingIdentity();
1002        try {
1003            SyncManager syncManager = getSyncManager();
1004            if (syncManager == null) {
1005                return null;
1006            }
1007            SyncStorageEngine.EndPoint info;
1008            if (!(account == null || authority == null)) {
1009                info = new SyncStorageEngine.EndPoint(account, authority, userId);
1010            } else {
1011                throw new IllegalArgumentException("Must call sync status with valid authority");
1012            }
1013            return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
1014        } finally {
1015            restoreCallingIdentity(identityToken);
1016        }
1017    }
1018
1019    @Override
1020    public boolean isSyncPending(Account account, String authority, ComponentName cname) {
1021        return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
1022    }
1023
1024    @Override
1025    public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
1026                                       int userId) {
1027        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1028                "no permission to read the sync stats");
1029        enforceCrossUserPermission(userId,
1030                "no permission to retrieve the sync settings for user " + userId);
1031        long identityToken = clearCallingIdentity();
1032        SyncManager syncManager = getSyncManager();
1033        if (syncManager == null) return false;
1034
1035        try {
1036            SyncStorageEngine.EndPoint info;
1037            if (!(account == null || authority == null)) {
1038                info = new SyncStorageEngine.EndPoint(account, authority, userId);
1039            } else {
1040                throw new IllegalArgumentException("Invalid authority specified");
1041            }
1042            return syncManager.getSyncStorageEngine().isSyncPending(info);
1043        } finally {
1044            restoreCallingIdentity(identityToken);
1045        }
1046    }
1047
1048    @Override
1049    public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1050        long identityToken = clearCallingIdentity();
1051        try {
1052            SyncManager syncManager = getSyncManager();
1053            if (syncManager != null && callback != null) {
1054                syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
1055            }
1056        } finally {
1057            restoreCallingIdentity(identityToken);
1058        }
1059    }
1060
1061    @Override
1062    public void removeStatusChangeListener(ISyncStatusObserver callback) {
1063        long identityToken = clearCallingIdentity();
1064        try {
1065            SyncManager syncManager = getSyncManager();
1066            if (syncManager != null && callback != null) {
1067                syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
1068            }
1069        } finally {
1070            restoreCallingIdentity(identityToken);
1071        }
1072    }
1073
1074    private @Nullable String getProviderPackageName(Uri uri) {
1075        final ProviderInfo pi = mContext.getPackageManager()
1076                .resolveContentProvider(uri.getAuthority(), 0);
1077        return (pi != null) ? pi.packageName : null;
1078    }
1079
1080    private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1081            String providerPackageName) {
1082        ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1083        if (userCache == null) {
1084            userCache = new ArrayMap<>();
1085            mCache.put(userId, userCache);
1086        }
1087        ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1088        if (packageCache == null) {
1089            packageCache = new ArrayMap<>();
1090            userCache.put(providerPackageName, packageCache);
1091        }
1092        return packageCache;
1093    }
1094
1095    private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1096        ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1097        if (userCache == null) return;
1098
1099        ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1100        if (packageCache == null) return;
1101
1102        if (uri != null) {
1103            for (int i = 0; i < packageCache.size();) {
1104                final Pair<String, Uri> key = packageCache.keyAt(i);
1105                if (key.second != null && key.second.toString().startsWith(uri.toString())) {
1106                    if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
1107                    packageCache.removeAt(i);
1108                } else {
1109                    i++;
1110                }
1111            }
1112        } else {
1113            if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
1114            packageCache.clear();
1115        }
1116    }
1117
1118    @Override
1119    public void putCache(String packageName, Uri key, Bundle value, int userId) {
1120        Bundle.setDefusable(value, true);
1121        enforceCrossUserPermission(userId, TAG);
1122        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1123        mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1124                packageName);
1125
1126        final String providerPackageName = getProviderPackageName(key);
1127        final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1128
1129        synchronized (mCache) {
1130            final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1131                    providerPackageName);
1132            if (value != null) {
1133                cache.put(fullKey, value);
1134            } else {
1135                cache.remove(fullKey);
1136            }
1137        }
1138    }
1139
1140    @Override
1141    public Bundle getCache(String packageName, Uri key, int userId) {
1142        enforceCrossUserPermission(userId, TAG);
1143        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1144        mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1145                packageName);
1146
1147        final String providerPackageName = getProviderPackageName(key);
1148        final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1149
1150        synchronized (mCache) {
1151            final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1152                    providerPackageName);
1153            return cache.get(fullKey);
1154        }
1155    }
1156
1157    /**
1158     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1159     * permission, if the userHandle is not for the caller.
1160     *
1161     * @param userHandle the user handle of the user we want to act on behalf of.
1162     * @param message the message to log on security exception.
1163     */
1164    private void enforceCrossUserPermission(int userHandle, String message) {
1165        final int callingUser = UserHandle.getCallingUserId();
1166        if (callingUser != userHandle) {
1167            mContext.enforceCallingOrSelfPermission(
1168                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1169        }
1170    }
1171
1172    /**
1173     * Hide this class since it is not part of api,
1174     * but current unittest framework requires it to be public
1175     * @hide
1176     */
1177    public static final class ObserverNode {
1178        private class ObserverEntry implements IBinder.DeathRecipient {
1179            public final IContentObserver observer;
1180            public final int uid;
1181            public final int pid;
1182            public final boolean notifyForDescendants;
1183            private final int userHandle;
1184            private final Object observersLock;
1185
1186            public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
1187                                 int _uid, int _pid, int _userHandle) {
1188                this.observersLock = observersLock;
1189                observer = o;
1190                uid = _uid;
1191                pid = _pid;
1192                userHandle = _userHandle;
1193                notifyForDescendants = n;
1194                try {
1195                    observer.asBinder().linkToDeath(this, 0);
1196                } catch (RemoteException e) {
1197                    binderDied();
1198                }
1199            }
1200
1201            @Override
1202            public void binderDied() {
1203                synchronized (observersLock) {
1204                    removeObserverLocked(observer);
1205                }
1206            }
1207
1208            public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1209                                   String name, String prefix, SparseIntArray pidCounts) {
1210                pidCounts.put(pid, pidCounts.get(pid)+1);
1211                pw.print(prefix); pw.print(name); pw.print(": pid=");
1212                pw.print(pid); pw.print(" uid=");
1213                pw.print(uid); pw.print(" user=");
1214                pw.print(userHandle); pw.print(" target=");
1215                pw.println(Integer.toHexString(System.identityHashCode(
1216                        observer != null ? observer.asBinder() : null)));
1217            }
1218        }
1219
1220        public static final int INSERT_TYPE = 0;
1221        public static final int UPDATE_TYPE = 1;
1222        public static final int DELETE_TYPE = 2;
1223
1224        private String mName;
1225        private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1226        private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1227
1228        public ObserverNode(String name) {
1229            mName = name;
1230        }
1231
1232        public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1233                               String name, String prefix, int[] counts, SparseIntArray pidCounts) {
1234            String innerName = null;
1235            if (mObservers.size() > 0) {
1236                if ("".equals(name)) {
1237                    innerName = mName;
1238                } else {
1239                    innerName = name + "/" + mName;
1240                }
1241                for (int i=0; i<mObservers.size(); i++) {
1242                    counts[1]++;
1243                    mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1244                            pidCounts);
1245                }
1246            }
1247            if (mChildren.size() > 0) {
1248                if (innerName == null) {
1249                    if ("".equals(name)) {
1250                        innerName = mName;
1251                    } else {
1252                        innerName = name + "/" + mName;
1253                    }
1254                }
1255                for (int i=0; i<mChildren.size(); i++) {
1256                    counts[0]++;
1257                    mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1258                            counts, pidCounts);
1259                }
1260            }
1261        }
1262
1263        private String getUriSegment(Uri uri, int index) {
1264            if (uri != null) {
1265                if (index == 0) {
1266                    return uri.getAuthority();
1267                } else {
1268                    return uri.getPathSegments().get(index - 1);
1269                }
1270            } else {
1271                return null;
1272            }
1273        }
1274
1275        private int countUriSegments(Uri uri) {
1276            if (uri == null) {
1277                return 0;
1278            }
1279            return uri.getPathSegments().size() + 1;
1280        }
1281
1282        // Invariant:  userHandle is either a hard user number or is USER_ALL
1283        public void addObserverLocked(Uri uri, IContentObserver observer,
1284                                      boolean notifyForDescendants, Object observersLock,
1285                                      int uid, int pid, int userHandle) {
1286            addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1287                    uid, pid, userHandle);
1288        }
1289
1290        private void addObserverLocked(Uri uri, int index, IContentObserver observer,
1291                                       boolean notifyForDescendants, Object observersLock,
1292                                       int uid, int pid, int userHandle) {
1293            // If this is the leaf node add the observer
1294            if (index == countUriSegments(uri)) {
1295                mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1296                        uid, pid, userHandle));
1297                return;
1298            }
1299
1300            // Look to see if the proper child already exists
1301            String segment = getUriSegment(uri, index);
1302            if (segment == null) {
1303                throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1304            }
1305            int N = mChildren.size();
1306            for (int i = 0; i < N; i++) {
1307                ObserverNode node = mChildren.get(i);
1308                if (node.mName.equals(segment)) {
1309                    node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1310                            observersLock, uid, pid, userHandle);
1311                    return;
1312                }
1313            }
1314
1315            // No child found, create one
1316            ObserverNode node = new ObserverNode(segment);
1317            mChildren.add(node);
1318            node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1319                    observersLock, uid, pid, userHandle);
1320        }
1321
1322        public boolean removeObserverLocked(IContentObserver observer) {
1323            int size = mChildren.size();
1324            for (int i = 0; i < size; i++) {
1325                boolean empty = mChildren.get(i).removeObserverLocked(observer);
1326                if (empty) {
1327                    mChildren.remove(i);
1328                    i--;
1329                    size--;
1330                }
1331            }
1332
1333            IBinder observerBinder = observer.asBinder();
1334            size = mObservers.size();
1335            for (int i = 0; i < size; i++) {
1336                ObserverEntry entry = mObservers.get(i);
1337                if (entry.observer.asBinder() == observerBinder) {
1338                    mObservers.remove(i);
1339                    // We no longer need to listen for death notifications. Remove it.
1340                    observerBinder.unlinkToDeath(entry, 0);
1341                    break;
1342                }
1343            }
1344
1345            if (mChildren.size() == 0 && mObservers.size() == 0) {
1346                return true;
1347            }
1348            return false;
1349        }
1350
1351        private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
1352                                              boolean observerWantsSelfNotifications, int flags,
1353                                              int targetUserHandle, ArrayList<ObserverCall> calls) {
1354            int N = mObservers.size();
1355            IBinder observerBinder = observer == null ? null : observer.asBinder();
1356            for (int i = 0; i < N; i++) {
1357                ObserverEntry entry = mObservers.get(i);
1358
1359                // Don't notify the observer if it sent the notification and isn't interested
1360                // in self notifications
1361                boolean selfChange = (entry.observer.asBinder() == observerBinder);
1362                if (selfChange && !observerWantsSelfNotifications) {
1363                    continue;
1364                }
1365
1366                // Does this observer match the target user?
1367                if (targetUserHandle == UserHandle.USER_ALL
1368                        || entry.userHandle == UserHandle.USER_ALL
1369                        || targetUserHandle == entry.userHandle) {
1370                    // Make sure the observer is interested in the notification
1371                    if (leaf) {
1372                        // If we are at the leaf: we always report, unless the sender has asked
1373                        // to skip observers that are notifying for descendants (since they will
1374                        // be sending another more specific URI for them).
1375                        if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1376                                && entry.notifyForDescendants) {
1377                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1378                                    + ": skip notify for descendants");
1379                            continue;
1380                        }
1381                    } else {
1382                        // If we are not at the leaf: we report if the observer says it wants
1383                        // to be notified for all descendants.
1384                        if (!entry.notifyForDescendants) {
1385                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1386                                    + ": not monitor descendants");
1387                            continue;
1388                        }
1389                    }
1390                    if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1391                            + " flags=" + Integer.toHexString(flags)
1392                            + " desc=" + entry.notifyForDescendants);
1393                    calls.add(new ObserverCall(this, entry.observer, selfChange,
1394                            UserHandle.getUserId(entry.uid)));
1395                }
1396            }
1397        }
1398
1399        /**
1400         * targetUserHandle is either a hard user handle or is USER_ALL
1401         */
1402        public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
1403                                           boolean observerWantsSelfNotifications, int flags,
1404                                           int targetUserHandle, ArrayList<ObserverCall> calls) {
1405            String segment = null;
1406            int segmentCount = countUriSegments(uri);
1407            if (index >= segmentCount) {
1408                // This is the leaf node, notify all observers
1409                if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
1410                collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1411                        flags, targetUserHandle, calls);
1412            } else if (index < segmentCount){
1413                segment = getUriSegment(uri, index);
1414                if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1415                        + segment);
1416                // Notify any observers at this level who are interested in descendants
1417                collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1418                        flags, targetUserHandle, calls);
1419            }
1420
1421            int N = mChildren.size();
1422            for (int i = 0; i < N; i++) {
1423                ObserverNode node = mChildren.get(i);
1424                if (segment == null || node.mName.equals(segment)) {
1425                    // We found the child,
1426                    node.collectObserversLocked(uri, index + 1, observer,
1427                            observerWantsSelfNotifications, flags, targetUserHandle, calls);
1428                    if (segment != null) {
1429                        break;
1430                    }
1431                }
1432            }
1433        }
1434    }
1435}
1436