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