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