1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.content;
18 
19 import static android.content.PermissionChecker.PERMISSION_GRANTED;
20 
21 import android.Manifest;
22 import android.accounts.Account;
23 import android.accounts.AccountManagerInternal;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.RequiresPermission;
27 import android.annotation.UserIdInt;
28 import android.app.ActivityManager;
29 import android.app.ActivityManager.RestrictionLevel;
30 import android.app.ActivityManagerInternal;
31 import android.app.AppGlobals;
32 import android.app.AppOpsManager;
33 import android.app.compat.CompatChanges;
34 import android.app.job.JobInfo;
35 import android.compat.annotation.ChangeId;
36 import android.compat.annotation.EnabledAfter;
37 import android.content.BroadcastReceiver;
38 import android.content.ComponentName;
39 import android.content.ContentResolver;
40 import android.content.ContentResolver.SyncExemption;
41 import android.content.Context;
42 import android.content.IContentService;
43 import android.content.ISyncStatusObserver;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.PeriodicSync;
47 import android.content.SyncAdapterType;
48 import android.content.SyncInfo;
49 import android.content.SyncRequest;
50 import android.content.SyncStatusInfo;
51 import android.content.pm.PackageManager;
52 import android.content.pm.ProviderInfo;
53 import android.database.IContentObserver;
54 import android.net.Uri;
55 import android.os.AppBackgroundRestrictionsInfo;
56 import android.os.Binder;
57 import android.os.Build;
58 import android.os.Bundle;
59 import android.os.FactoryTest;
60 import android.os.IBinder;
61 import android.os.Process;
62 import android.os.RemoteException;
63 import android.os.ResultReceiver;
64 import android.os.ShellCallback;
65 import android.os.UserHandle;
66 import android.text.TextUtils;
67 import android.text.format.DateUtils;
68 import android.util.ArrayMap;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.Pair;
72 import android.util.Slog;
73 import android.util.SparseArray;
74 import android.util.SparseIntArray;
75 
76 import com.android.internal.annotations.GuardedBy;
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.os.BackgroundThread;
79 import com.android.internal.os.BinderDeathDispatcher;
80 import com.android.internal.util.ArrayUtils;
81 import com.android.internal.util.DumpUtils;
82 import com.android.internal.util.FrameworkStatsLog;
83 import com.android.internal.util.IndentingPrintWriter;
84 import com.android.server.LocalServices;
85 import com.android.server.SystemService;
86 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
87 
88 import java.io.FileDescriptor;
89 import java.io.PrintWriter;
90 import java.util.ArrayList;
91 import java.util.Arrays;
92 import java.util.Collections;
93 import java.util.Comparator;
94 import java.util.List;
95 import java.util.Objects;
96 
97 /**
98  * {@hide}
99  */
100 public final class ContentService extends IContentService.Stub {
101     static final String TAG = "ContentService";
102     static final boolean DEBUG = false;
103 
104     /** Do a WTF if a single observer is registered more than this times. */
105     private static final int TOO_MANY_OBSERVERS_THRESHOLD = 1000;
106 
107     /**
108      * Delay to apply to content change notifications dispatched to apps running
109      * in the background. This is used to help prevent stampeding when the user
110      * is performing CPU/RAM intensive foreground tasks, such as when capturing
111      * burst photos.
112      */
113     private static final long BACKGROUND_OBSERVER_DELAY = 10 * DateUtils.SECOND_IN_MILLIS;
114 
115     /**
116      * Enables checking for account access for the calling uid on all sync-related APIs.
117      */
118     @ChangeId
119     @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
120     public static final long ACCOUNT_ACCESS_CHECK_CHANGE_ID = 201794303L;
121 
122     /**
123      * Enables checking for authority access for the calling uid on all sync-related APIs.
124      */
125     @ChangeId
126     @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
127     public static final long AUTHORITY_ACCESS_CHECK_CHANGE_ID = 207133734L;
128 
129     public static class Lifecycle extends SystemService {
130         private ContentService mService;
131 
Lifecycle(Context context)132         public Lifecycle(Context context) {
133             super(context);
134         }
135 
136         @Override
onStart()137         public void onStart() {
138             final boolean factoryTest = (FactoryTest
139                     .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
140             mService = new ContentService(getContext(), factoryTest);
141             publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
142         }
143 
144         @Override
onBootPhase(int phase)145         public void onBootPhase(int phase) {
146             mService.onBootPhase(phase);
147         }
148 
149         @Override
onUserStarting(@onNull TargetUser user)150         public void onUserStarting(@NonNull TargetUser user) {
151             mService.onStartUser(user.getUserIdentifier());
152         }
153 
154         @Override
onUserUnlocking(@onNull TargetUser user)155         public void onUserUnlocking(@NonNull TargetUser user) {
156             mService.onUnlockUser(user.getUserIdentifier());
157         }
158 
159         @Override
onUserStopping(@onNull TargetUser user)160         public void onUserStopping(@NonNull TargetUser user) {
161             mService.onStopUser(user.getUserIdentifier());
162         }
163 
164         @Override
onUserStopped(@onNull TargetUser user)165         public void onUserStopped(@NonNull TargetUser user) {
166             synchronized (mService.mCache) {
167                 mService.mCache.remove(user.getUserIdentifier());
168             }
169         }
170     }
171 
172     private Context mContext;
173     private boolean mFactoryTest;
174 
175     private final ObserverNode mRootNode = new ObserverNode("");
176 
177     private SyncManager mSyncManager = null;
178     private final Object mSyncManagerLock = new Object();
179 
180     private final AccountManagerInternal mAccountManagerInternal;
181 
182     private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
183             new BinderDeathDispatcher<>();
184 
185     @GuardedBy("sObserverLeakDetectedUid")
186     private static final ArraySet<Integer> sObserverLeakDetectedUid = new ArraySet<>(0);
187 
188     /**
189      * Map from userId to providerPackageName to [clientPackageName, uri] to
190      * value. This structure is carefully optimized to keep invalidation logic
191      * as cheap as possible.
192      */
193     @GuardedBy("mCache")
194     private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
195             mCache = new SparseArray<>();
196 
197     private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
198         @Override
199         public void onReceive(Context context, Intent intent) {
200             synchronized (mCache) {
201                 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
202                     mCache.clear();
203                 } else {
204                     final Uri data = intent.getData();
205                     if (data != null) {
206                         final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
207                                 UserHandle.USER_NULL);
208                         final String packageName = data.getSchemeSpecificPart();
209                         invalidateCacheLocked(userId, packageName, null);
210                     }
211                 }
212             }
213         }
214     };
215 
getSyncManager()216     private SyncManager getSyncManager() {
217         synchronized(mSyncManagerLock) {
218             if (mSyncManager == null) {
219                 mSyncManager = new SyncManager(mContext, mFactoryTest);
220             }
221             return mSyncManager;
222         }
223     }
224 
onStartUser(int userHandle)225     void onStartUser(int userHandle) {
226         if (mSyncManager != null) mSyncManager.onStartUser(userHandle);
227     }
228 
onUnlockUser(int userHandle)229     void onUnlockUser(int userHandle) {
230         if (mSyncManager != null) mSyncManager.onUnlockUser(userHandle);
231     }
232 
onStopUser(int userHandle)233     void onStopUser(int userHandle) {
234         if (mSyncManager != null) mSyncManager.onStopUser(userHandle);
235     }
236 
237     @Override
dump(FileDescriptor fd, PrintWriter pw_, String[] args)238     protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
239         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw_)) return;
240         final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, "  ");
241 
242         final boolean dumpAll = ArrayUtils.contains(args, "-a");
243 
244         // This makes it so that future permission checks will be in the context of this
245         // process rather than the caller's process. We will restore this before returning.
246         final long identityToken = clearCallingIdentity();
247         try {
248             if (mSyncManager == null) {
249                 pw.println("SyncManager not available yet");
250             } else {
251                 mSyncManager.dump(fd, pw, dumpAll);
252             }
253             pw.println();
254             pw.println("Observer tree:");
255             synchronized (mRootNode) {
256                 int[] counts = new int[2];
257                 final SparseIntArray pidCounts = new SparseIntArray();
258                 mRootNode.dumpLocked(fd, pw, args, "", "  ", counts, pidCounts);
259                 pw.println();
260                 ArrayList<Integer> sorted = new ArrayList<Integer>();
261                 for (int i=0; i<pidCounts.size(); i++) {
262                     sorted.add(pidCounts.keyAt(i));
263                 }
264                 Collections.sort(sorted, new Comparator<Integer>() {
265                     @Override
266                     public int compare(Integer lhs, Integer rhs) {
267                         int lc = pidCounts.get(lhs);
268                         int rc = pidCounts.get(rhs);
269                         if (lc < rc) {
270                             return 1;
271                         } else if (lc > rc) {
272                             return -1;
273                         }
274                         return 0;
275                     }
276 
277                 });
278                 for (int i=0; i<sorted.size(); i++) {
279                     int pid = sorted.get(i);
280                     pw.print("  pid "); pw.print(pid); pw.print(": ");
281                     pw.print(pidCounts.get(pid)); pw.println(" observers");
282                 }
283                 pw.println();
284                 pw.increaseIndent();
285                 pw.print("Total number of nodes: "); pw.println(counts[0]);
286                 pw.print("Total number of observers: "); pw.println(counts[1]);
287 
288                 sObserverDeathDispatcher.dump(pw);
289                 pw.decreaseIndent();
290             }
291             synchronized (sObserverLeakDetectedUid) {
292                 pw.println();
293                 pw.print("Observer leaking UIDs: ");
294                 pw.println(sObserverLeakDetectedUid.toString());
295             }
296 
297             synchronized (mCache) {
298                 pw.println();
299                 pw.println("Cached content:");
300                 pw.increaseIndent();
301                 for (int i = 0; i < mCache.size(); i++) {
302                     pw.println("User " + mCache.keyAt(i) + ":");
303                     pw.increaseIndent();
304                     pw.println(mCache.valueAt(i));
305                     pw.decreaseIndent();
306                 }
307                 pw.decreaseIndent();
308             }
309         } finally {
310             restoreCallingIdentity(identityToken);
311         }
312     }
313 
ContentService(Context context, boolean factoryTest)314     /*package*/ ContentService(Context context, boolean factoryTest) {
315         mContext = context;
316         mFactoryTest = factoryTest;
317 
318         // Let the package manager query for the sync adapters for a given authority
319         // as we grant default permissions to sync adapters for specific authorities.
320         final LegacyPermissionManagerInternal permissionManagerInternal =
321                 LocalServices.getService(LegacyPermissionManagerInternal.class);
322         permissionManagerInternal.setSyncAdapterPackagesProvider((authority, userId) -> {
323             return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
324         });
325 
326         final IntentFilter packageFilter = new IntentFilter();
327         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
328         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
329         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
330         packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
331         packageFilter.addDataScheme("package");
332         mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
333                 packageFilter, null, null);
334 
335         final IntentFilter localeFilter = new IntentFilter();
336         localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
337         mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
338                 localeFilter, null, null);
339 
340         mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
341     }
342 
onBootPhase(int phase)343     void onBootPhase(int phase) {
344         switch (phase) {
345             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
346                 getSyncManager();
347                 break;
348         }
349         if (mSyncManager != null) {
350             mSyncManager.onBootPhase(phase);
351         }
352     }
353 
354     /**
355      * Register a content observer tied to a specific user's view of the provider.
356      * @param userHandle the user whose view of the provider is to be observed.  May be
357      *     the calling user without requiring any permission, otherwise the caller needs to
358      *     hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
359      *     Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
360      *     are forbidden.
361      */
362     @Override
registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle, int targetSdkVersion)363     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
364             IContentObserver observer, int userHandle, int targetSdkVersion) {
365         if (observer == null || uri == null) {
366             throw new IllegalArgumentException("You must pass a valid uri and observer");
367         }
368 
369         final int uid = Binder.getCallingUid();
370         final int pid = Binder.getCallingPid();
371 
372         userHandle = handleIncomingUser(uri, pid, uid,
373                 Intent.FLAG_GRANT_READ_URI_PERMISSION, true, userHandle);
374 
375         final String msg = LocalServices.getService(ActivityManagerInternal.class)
376                 .checkContentProviderAccess(uri.getAuthority(), userHandle);
377         if (msg != null) {
378             if (targetSdkVersion >= Build.VERSION_CODES.O) {
379                 throw new SecurityException(msg);
380             } else {
381                 if (msg.startsWith("Failed to find provider")) {
382                     // Sigh, we need to quietly let apps targeting older API
383                     // levels notify on non-existent providers.
384                 } else {
385                     Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
386                     return;
387                 }
388             }
389         }
390 
391         synchronized (mRootNode) {
392             mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
393                     uid, pid, userHandle);
394             if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
395                     " with notifyForDescendants " + notifyForDescendants);
396         }
397     }
398 
registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer)399     public void registerContentObserver(Uri uri, boolean notifyForDescendants,
400                                         IContentObserver observer) {
401         registerContentObserver(uri, notifyForDescendants, observer,
402                 UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT);
403     }
404 
405     @Override
unregisterContentObserver(IContentObserver observer)406     public void unregisterContentObserver(IContentObserver observer) {
407         if (observer == null) {
408             throw new IllegalArgumentException("You must pass a valid observer");
409         }
410         synchronized (mRootNode) {
411             mRootNode.removeObserverLocked(observer);
412             if (false) Log.v(TAG, "Unregistered observer " + observer);
413         }
414     }
415 
416     /**
417      * Notify observers of a particular user's view of the provider.
418      * @param userHandle the user whose view of the provider is to be notified.  May be
419      *     the calling user without requiring any permission, otherwise the caller needs to
420      *     hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
421      *     Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
422      *     allowed.
423      */
424     @Override
notifyChange(Uri[] uris, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userId, int targetSdkVersion, String callingPackage)425     public void notifyChange(Uri[] uris, IContentObserver observer,
426             boolean observerWantsSelfNotifications, int flags, int userId,
427             int targetSdkVersion, String callingPackage) {
428         if (DEBUG) {
429             Slog.d(TAG, "Notifying update of " + Arrays.toString(uris) + " for user " + userId
430                     + ", observer " + observer + ", flags " + Integer.toHexString(flags));
431         }
432 
433         final int callingUid = Binder.getCallingUid();
434         final int callingPid = Binder.getCallingPid();
435         final int callingUserId = UserHandle.getCallingUserId();
436 
437         // Set of notification events that we need to dispatch
438         final ObserverCollector collector = new ObserverCollector();
439 
440         // Set of content provider authorities that we've validated the caller
441         // has access to, mapped to the package name hosting that provider
442         final ArrayMap<Pair<String, Integer>, String> validatedProviders = new ArrayMap<>();
443 
444         for (Uri uri : uris) {
445             // Validate that calling app has access to this provider
446             final int resolvedUserId = handleIncomingUser(uri, callingPid, callingUid,
447                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userId);
448             final Pair<String, Integer> provider = Pair.create(uri.getAuthority(), resolvedUserId);
449             if (!validatedProviders.containsKey(provider)) {
450                 final String msg = LocalServices.getService(ActivityManagerInternal.class)
451                         .checkContentProviderAccess(uri.getAuthority(), resolvedUserId);
452                 if (msg != null) {
453                     if (targetSdkVersion >= Build.VERSION_CODES.O) {
454                         throw new SecurityException(msg);
455                     } else {
456                         if (msg.startsWith("Failed to find provider")) {
457                             // Sigh, we need to quietly let apps targeting older API
458                             // levels notify on non-existent providers.
459                         } else {
460                             Log.w(TAG, "Ignoring notify for " + uri + " from "
461                                     + callingUid + ": " + msg);
462                             continue;
463                         }
464                     }
465                 }
466 
467                 // Remember that we've validated this access
468                 final String packageName = getProviderPackageName(uri, resolvedUserId);
469                 validatedProviders.put(provider, packageName);
470             }
471 
472             // No concerns raised above, so caller has access; let's collect the
473             // notifications that should be dispatched
474             synchronized (mRootNode) {
475                 final int segmentCount = ObserverNode.countUriSegments(uri);
476                 mRootNode.collectObserversLocked(uri, segmentCount, 0, observer,
477                         observerWantsSelfNotifications, flags, resolvedUserId, collector);
478             }
479         }
480 
481         final long token = clearCallingIdentity();
482         try {
483             // Actually dispatch all the notifications we collected
484             collector.dispatch();
485 
486             final SyncManager syncManager = getSyncManager();
487             for (int i = 0; i < validatedProviders.size(); i++) {
488                 final String authority = validatedProviders.keyAt(i).first;
489                 final int resolvedUserId = validatedProviders.keyAt(i).second;
490                 final String packageName = validatedProviders.valueAt(i);
491 
492                 // Kick off sync adapters for any authorities we touched
493                 if ((flags & ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
494                     syncManager.scheduleLocalSync(null /* all accounts */, callingUserId,
495                             callingUid,
496                             authority, getSyncExemptionForCaller(callingUid),
497                             callingUid, callingPid, callingPackage);
498                 }
499 
500                 // Invalidate caches for any authorities we touched
501                 synchronized (mCache) {
502                     for (Uri uri : uris) {
503                         if (Objects.equals(uri.getAuthority(), authority)) {
504                             invalidateCacheLocked(resolvedUserId, packageName, uri);
505                         }
506                     }
507                 }
508             }
509         } finally {
510             Binder.restoreCallingIdentity(token);
511         }
512     }
513 
checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle)514     private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
515         try {
516             return ActivityManager.getService().checkUriPermission(
517                     uri, pid, uid, modeFlags, userHandle, null);
518         } catch (RemoteException e) {
519             return PackageManager.PERMISSION_DENIED;
520         }
521     }
522 
523     /**
524      * Collection of detected change notifications that should be delivered.
525      * <p>
526      * To help reduce Binder transaction overhead, this class clusters together
527      * multiple {@link Uri} where all other arguments are identical.
528      */
529     @VisibleForTesting
530     public static class ObserverCollector {
531         private final ArrayMap<Key, List<Uri>> collected = new ArrayMap<>();
532 
533         private static class Key {
534             final IContentObserver observer;
535             final int uid;
536             final boolean selfChange;
537             final int flags;
538             final int userId;
539 
Key(IContentObserver observer, int uid, boolean selfChange, int flags, int userId)540             Key(IContentObserver observer, int uid, boolean selfChange, int flags, int userId) {
541                 this.observer = observer;
542                 this.uid = uid;
543                 this.selfChange = selfChange;
544                 this.flags = flags;
545                 this.userId = userId;
546             }
547 
548             @Override
equals(Object o)549             public boolean equals(Object o) {
550                 if (!(o instanceof Key)) {
551                     return false;
552                 }
553                 final Key other = (Key) o;
554                 return Objects.equals(observer, other.observer)
555                         && (uid == other.uid)
556                         && (selfChange == other.selfChange)
557                         && (flags == other.flags)
558                         && (userId == other.userId);
559             }
560 
561             @Override
hashCode()562             public int hashCode() {
563                 return Objects.hash(observer, uid, selfChange, flags, userId);
564             }
565         }
566 
collect(IContentObserver observer, int uid, boolean selfChange, Uri uri, int flags, int userId)567         public void collect(IContentObserver observer, int uid, boolean selfChange, Uri uri,
568                 int flags, int userId) {
569             final Key key = new Key(observer, uid, selfChange, flags, userId);
570             List<Uri> value = collected.get(key);
571             if (value == null) {
572                 value = new ArrayList<>();
573                 collected.put(key, value);
574             }
575             value.add(uri);
576         }
577 
dispatch()578         public void dispatch() {
579             for (int i = 0; i < collected.size(); i++) {
580                 final Key key = collected.keyAt(i);
581                 final List<Uri> value = collected.valueAt(i);
582 
583                 final Runnable task = () -> {
584                     try {
585                         key.observer.onChangeEtc(key.selfChange,
586                                 value.toArray(new Uri[value.size()]), key.flags, key.userId);
587                     } catch (RemoteException ignored) {
588                     }
589                 };
590 
591                 // Immediately dispatch notifications to foreground apps that
592                 // are important to the user; all other background observers are
593                 // delayed to avoid stampeding
594                 final boolean noDelay = (key.flags & ContentResolver.NOTIFY_NO_DELAY) != 0;
595                 final int procState = LocalServices.getService(ActivityManagerInternal.class)
596                         .getUidProcessState(key.uid);
597                 if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) {
598                     task.run();
599                 } else {
600                     BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY);
601                 }
602             }
603         }
604     }
605 
606     @Override
requestSync(Account account, String authority, Bundle extras, String callingPackage)607     public void requestSync(Account account, String authority, Bundle extras,
608             String callingPackage) {
609         Bundle.setDefusable(extras, true);
610         ContentResolver.validateSyncExtrasBundle(extras);
611         int userId = UserHandle.getCallingUserId();
612         final int callingUid = Binder.getCallingUid();
613         final int callingPid = Binder.getCallingPid();
614 
615         if (!hasAccountAccess(true, account, callingUid)) {
616             return;
617         }
618         if (!hasAuthorityAccess(authority, callingUid, userId)) {
619             return;
620         }
621 
622         validateExtras(callingUid, extras);
623         final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
624 
625         // This makes it so that future permission checks will be in the context of this
626         // process rather than the caller's process. We will restore this before returning.
627         final long identityToken = clearCallingIdentity();
628         try {
629             getSyncManager().scheduleSync(account, userId, callingUid, authority, extras,
630                     SyncStorageEngine.AuthorityInfo.UNDEFINED,
631                     syncExemption, callingUid, callingPid, callingPackage);
632         } finally {
633             restoreCallingIdentity(identityToken);
634         }
635     }
636 
637     /**
638      * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
639      * either:
640      *   periodic OR one-off sync.
641      * and
642      *   anonymous OR provider sync.
643      * Depending on the request, we enqueue to suit in the SyncManager.
644      * @param request The request object. Validation of this object is done by its builder.
645      */
646     @Override
sync(SyncRequest request, String callingPackage)647     public void sync(SyncRequest request, String callingPackage) {
648         syncAsUser(request, UserHandle.getCallingUserId(), callingPackage);
649     }
650 
clampPeriod(long period)651     private long clampPeriod(long period) {
652         long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
653         if (period < minPeriod) {
654             Slog.w(TAG, "Requested poll frequency of " + period
655                     + " seconds being rounded up to " + minPeriod + "s.");
656             period = minPeriod;
657         }
658         return period;
659     }
660 
661     /**
662      * If the user id supplied is different to the calling user, the caller must hold the
663      * INTERACT_ACROSS_USERS_FULL permission.
664      */
665     @Override
syncAsUser(SyncRequest request, int userId, String callingPackage)666     public void syncAsUser(SyncRequest request, int userId, String callingPackage) {
667         enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
668 
669         final int callingUid = Binder.getCallingUid();
670         final int callingPid = Binder.getCallingPid();
671         if (!hasAccountAccess(true, request.getAccount(), callingUid)) {
672             return;
673         }
674         if (!hasAuthorityAccess(request.getProvider(), callingUid, userId)) {
675             return;
676         }
677 
678         final Bundle extras = request.getBundle();
679         validateExtras(callingUid, extras);
680         final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
681 
682         // This makes it so that future permission checks will be in the context of this
683         // process rather than the caller's process. We will restore this before returning.
684         final long identityToken = clearCallingIdentity();
685         try {
686             long flextime = request.getSyncFlexTime();
687             long runAtTime = request.getSyncRunTime();
688             if (request.isPeriodic()) {
689                 mContext.enforceCallingOrSelfPermission(
690                         Manifest.permission.WRITE_SYNC_SETTINGS,
691                         "no permission to write the sync settings");
692                 SyncStorageEngine.EndPoint info;
693                 info = new SyncStorageEngine.EndPoint(
694                         request.getAccount(), request.getProvider(), userId);
695 
696                 runAtTime = clampPeriod(runAtTime);
697                 // Schedule periodic sync.
698                 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
699                         flextime, extras);
700             } else {
701                 getSyncManager().scheduleSync(
702                         request.getAccount(), userId, callingUid, request.getProvider(), extras,
703                         SyncStorageEngine.AuthorityInfo.UNDEFINED,
704                         syncExemption, callingUid, callingPid, callingPackage);
705             }
706         } finally {
707             restoreCallingIdentity(identityToken);
708         }
709     }
710 
711     /**
712      * Clear all scheduled sync operations that match the uri and cancel the active sync
713      * if they match the authority and account, if they are present.
714      *
715      * @param account filter the pending and active syncs to cancel using this account, or null.
716      * @param authority filter the pending and active syncs to cancel using this authority, or
717      * null.
718      * @param cname cancel syncs running on this service, or null for provider/account.
719      */
720     @Override
cancelSync(Account account, String authority, ComponentName cname)721     public void cancelSync(Account account, String authority, ComponentName cname) {
722         cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
723     }
724 
725     /**
726      * Clear all scheduled sync operations that match the uri and cancel the active sync
727      * if they match the authority and account, if they are present.
728      *
729      * <p> If the user id supplied is different to the calling user, the caller must hold the
730      * INTERACT_ACROSS_USERS_FULL permission.
731      *
732      * @param account filter the pending and active syncs to cancel using this account, or null.
733      * @param authority filter the pending and active syncs to cancel using this authority, or
734      * null.
735      * @param userId the user id for which to cancel sync operations.
736      * @param cname cancel syncs running on this service, or null for provider/account.
737      */
738     @Override
cancelSyncAsUser(Account account, String authority, ComponentName cname, int userId)739     public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
740                                  int userId) {
741         if (authority != null && authority.length() == 0) {
742             throw new IllegalArgumentException("Authority must be non-empty");
743         }
744         enforceCrossUserPermission(userId,
745                 "no permission to modify the sync settings for user " + userId);
746 
747         if (cname != null) {
748             Slog.e(TAG, "cname not null.");
749             return;
750         }
751 
752         // This makes it so that future permission checks will be in the context of this
753         // process rather than the caller's process. We will restore this before returning.
754         final long identityToken = clearCallingIdentity();
755         try {
756             SyncStorageEngine.EndPoint info;
757             info = new SyncStorageEngine.EndPoint(account, authority, userId);
758             getSyncManager().clearScheduledSyncOperations(info);
759             getSyncManager().cancelActiveSync(info, null /* all syncs for this adapter */, "API");
760         } finally {
761             restoreCallingIdentity(identityToken);
762         }
763     }
764 
765     @Override
cancelRequest(SyncRequest request)766     public void cancelRequest(SyncRequest request) {
767         if (request.isPeriodic()) {
768             mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
769                     "no permission to write the sync settings");
770         }
771 
772         final int callingUid = Binder.getCallingUid();
773         Bundle extras = new Bundle(request.getBundle());
774         validateExtras(callingUid, extras);
775 
776         int userId = UserHandle.getCallingUserId();
777         final long identityToken = clearCallingIdentity();
778         try {
779             SyncStorageEngine.EndPoint info;
780 
781             Account account = request.getAccount();
782             String provider = request.getProvider();
783             info = new SyncStorageEngine.EndPoint(account, provider, userId);
784             if (request.isPeriodic()) {
785                 // Remove periodic sync.
786                 getSyncManager().removePeriodicSync(info, extras,
787                         "cancelRequest() by uid=" + callingUid);
788             }
789             // Cancel active syncs and clear pending syncs from the queue.
790             getSyncManager().cancelScheduledSyncOperation(info, extras);
791             getSyncManager().cancelActiveSync(info, extras, "API");
792         } finally {
793             restoreCallingIdentity(identityToken);
794         }
795     }
796 
797     /**
798      * Get information about the SyncAdapters that are known to the system.
799      * @return an array of SyncAdapters that have registered with the system
800      */
801     @Override
getSyncAdapterTypes()802     public SyncAdapterType[] getSyncAdapterTypes() {
803         return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
804     }
805 
806     /**
807      * Get information about the SyncAdapters that are known to the system for a particular user.
808      *
809      * <p> If the user id supplied is different to the calling user, the caller must hold the
810      * INTERACT_ACROSS_USERS_FULL permission.
811      *
812      * @return an array of SyncAdapters that have registered with the system
813      */
814     @Override
getSyncAdapterTypesAsUser(int userId)815     public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
816         enforceCrossUserPermission(userId,
817                 "no permission to read sync settings for user " + userId);
818 
819         // This makes it so that future permission checks will be in the context of this
820         // process rather than the caller's process. We will restore this before returning.
821         final int callingUid = Binder.getCallingUid();
822         final long identityToken = clearCallingIdentity();
823         try {
824             return getSyncManager().getSyncAdapterTypes(callingUid, userId);
825         } finally {
826             restoreCallingIdentity(identityToken);
827         }
828     }
829 
830     @Override
getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId)831     public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
832         enforceCrossUserPermission(userId,
833                 "no permission to read sync settings for user " + userId);
834 
835         // This makes it so that future permission checks will be in the context of this
836         // process rather than the caller's process. We will restore this before returning.
837         final int callingUid = Binder.getCallingUid();
838         final long identityToken = clearCallingIdentity();
839         try {
840             return getSyncManager().getSyncAdapterPackagesForAuthorityAsUser(authority, callingUid,
841                     userId);
842         } finally {
843             restoreCallingIdentity(identityToken);
844         }
845     }
846 
847     @Override
848     @Nullable
getSyncAdapterPackageAsUser(@onNull String accountType, @NonNull String authority, @UserIdInt int userId)849     public String getSyncAdapterPackageAsUser(@NonNull String accountType,
850             @NonNull String authority, @UserIdInt int userId) {
851         enforceCrossUserPermission(userId,
852                 "no permission to read sync settings for user " + userId);
853 
854         final int callingUid = Binder.getCallingUid();
855         final long identityToken = clearCallingIdentity();
856         try {
857             return getSyncManager().getSyncAdapterPackageAsUser(accountType, authority,
858                     callingUid, userId);
859         } finally {
860             restoreCallingIdentity(identityToken);
861         }
862     }
863 
864     @Override
getSyncAutomatically(Account account, String providerName)865     public boolean getSyncAutomatically(Account account, String providerName) {
866         return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
867     }
868 
869     /**
870      * If the user id supplied is different to the calling user, the caller must hold the
871      * INTERACT_ACROSS_USERS_FULL permission.
872      */
873     @Override
getSyncAutomaticallyAsUser(Account account, String providerName, int userId)874     public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
875         enforceCrossUserPermission(userId,
876                 "no permission to read the sync settings for user " + userId);
877         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
878                 "no permission to read the sync settings");
879 
880         final int callingUid = Binder.getCallingUid();
881         if (!hasAccountAccess(true, account, callingUid)) {
882             return false;
883         }
884         if (!hasAuthorityAccess(providerName, callingUid, userId)) {
885             return false;
886         }
887 
888         final long identityToken = clearCallingIdentity();
889         try {
890             return getSyncManager().getSyncStorageEngine()
891                     .getSyncAutomatically(account, userId, providerName);
892         } finally {
893             restoreCallingIdentity(identityToken);
894         }
895     }
896 
897     @Override
setSyncAutomatically(Account account, String providerName, boolean sync)898     public void setSyncAutomatically(Account account, String providerName, boolean sync) {
899         setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
900     }
901 
902     @Override
setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync, int userId)903     public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
904                                            int userId) {
905         if (TextUtils.isEmpty(providerName)) {
906             throw new IllegalArgumentException("Authority must be non-empty");
907         }
908         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
909                 "no permission to write the sync settings");
910         enforceCrossUserPermission(userId,
911                 "no permission to modify the sync settings for user " + userId);
912 
913         final int callingUid = Binder.getCallingUid();
914         final int callingPid = Binder.getCallingPid();
915         if (!hasAccountAccess(true, account, callingUid)) {
916             return;
917         }
918         if (!hasAuthorityAccess(providerName, callingUid, userId)) {
919             return;
920         }
921 
922         final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);
923 
924         final long identityToken = clearCallingIdentity();
925         try {
926             getSyncManager().getSyncStorageEngine().setSyncAutomatically(account, userId,
927                     providerName, sync, syncExemptionFlag, callingUid, callingPid);
928         } finally {
929             restoreCallingIdentity(identityToken);
930         }
931     }
932 
933     /** Old API. Schedule periodic sync with default flexMillis time. */
934     @Override
addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency)935     public void addPeriodicSync(Account account, String authority, Bundle extras,
936                                 long pollFrequency) {
937         Bundle.setDefusable(extras, true);
938         if (account == null) {
939             throw new IllegalArgumentException("Account must not be null");
940         }
941         if (TextUtils.isEmpty(authority)) {
942             throw new IllegalArgumentException("Authority must not be empty.");
943         }
944         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
945                 "no permission to write the sync settings");
946 
947         final int callingUid = Binder.getCallingUid();
948         final int userId = UserHandle.getCallingUserId();
949         if (!hasAccountAccess(true, account, callingUid)) {
950             return;
951         }
952         if (!hasAuthorityAccess(authority, callingUid, userId)) {
953             return;
954         }
955 
956         validateExtras(callingUid, extras);
957 
958         pollFrequency = clampPeriod(pollFrequency);
959         long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
960 
961         final long identityToken = clearCallingIdentity();
962         try {
963             SyncStorageEngine.EndPoint info =
964                     new SyncStorageEngine.EndPoint(account, authority, userId);
965             getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
966                     defaultFlex, extras);
967         } finally {
968             restoreCallingIdentity(identityToken);
969         }
970     }
971 
972     @Override
removePeriodicSync(Account account, String authority, Bundle extras)973     public void removePeriodicSync(Account account, String authority, Bundle extras) {
974         Bundle.setDefusable(extras, true);
975         if (account == null) {
976             throw new IllegalArgumentException("Account must not be null");
977         }
978         if (TextUtils.isEmpty(authority)) {
979             throw new IllegalArgumentException("Authority must not be empty");
980         }
981         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
982                 "no permission to write the sync settings");
983 
984         final int callingUid = Binder.getCallingUid();
985         final int userId = UserHandle.getCallingUserId();
986         if (!hasAccountAccess(true, account, callingUid)) {
987             return;
988         }
989         if (!hasAuthorityAccess(authority, callingUid, userId)) {
990             return;
991         }
992 
993         validateExtras(callingUid, extras);
994 
995         final long identityToken = clearCallingIdentity();
996         try {
997             getSyncManager().removePeriodicSync(
998                     new SyncStorageEngine.EndPoint(account, authority, userId),
999                     extras, "removePeriodicSync() by uid=" + callingUid);
1000         } finally {
1001             restoreCallingIdentity(identityToken);
1002         }
1003     }
1004 
1005     @Override
getPeriodicSyncs(Account account, String providerName, ComponentName cname)1006     public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
1007                                                ComponentName cname) {
1008         if (account == null) {
1009             throw new IllegalArgumentException("Account must not be null");
1010         }
1011         if (TextUtils.isEmpty(providerName)) {
1012             throw new IllegalArgumentException("Authority must not be empty");
1013         }
1014         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
1015                 "no permission to read the sync settings");
1016 
1017         final int callingUid = Binder.getCallingUid();
1018         final int userId = UserHandle.getCallingUserId();
1019         if (!hasAccountAccess(true, account, callingUid)) {
1020             return new ArrayList<>(); // return a new empty list for consistent behavior
1021         }
1022         if (!hasAuthorityAccess(providerName, callingUid, userId)) {
1023             return new ArrayList<>();
1024         }
1025 
1026         final long identityToken = clearCallingIdentity();
1027         try {
1028             return getSyncManager().getPeriodicSyncs(
1029                     new SyncStorageEngine.EndPoint(account, providerName, userId));
1030         } finally {
1031             restoreCallingIdentity(identityToken);
1032         }
1033     }
1034 
1035     @Override
getIsSyncable(Account account, String providerName)1036     public int getIsSyncable(Account account, String providerName) {
1037         return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
1038     }
1039 
1040     /**
1041      * If the user id supplied is different to the calling user, the caller must hold the
1042      * INTERACT_ACROSS_USERS_FULL permission.
1043      */
1044     @Override
getIsSyncableAsUser(Account account, String providerName, int userId)1045     public int getIsSyncableAsUser(Account account, String providerName, int userId) {
1046         enforceCrossUserPermission(userId,
1047                 "no permission to read the sync settings for user " + userId);
1048         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
1049                 "no permission to read the sync settings");
1050 
1051         final int callingUid = Binder.getCallingUid();
1052         if (!hasAccountAccess(true, account, callingUid)) {
1053             return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE; // to keep behavior consistent
1054         }
1055         if (!hasAuthorityAccess(providerName, callingUid, userId)) {
1056             return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1057         }
1058 
1059         final long identityToken = clearCallingIdentity();
1060         try {
1061             return getSyncManager().computeSyncable(account, userId, providerName, false,
1062                     /*checkStoppedState=*/ false);
1063         } finally {
1064             restoreCallingIdentity(identityToken);
1065         }
1066     }
1067 
1068     @Override
setIsSyncable(Account account, String providerName, int syncable)1069     public void setIsSyncable(Account account, String providerName, int syncable) {
1070         setIsSyncableAsUser(account, providerName, syncable, UserHandle.getCallingUserId());
1071     }
1072 
1073     /**
1074      * @hide
1075      */
1076     @Override
setIsSyncableAsUser(Account account, String providerName, int syncable, int userId)1077     public void setIsSyncableAsUser(Account account, String providerName, int syncable,
1078             int userId) {
1079         if (TextUtils.isEmpty(providerName)) {
1080             throw new IllegalArgumentException("Authority must not be empty");
1081         }
1082         enforceCrossUserPermission(userId,
1083                 "no permission to set the sync settings for user " + userId);
1084         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
1085                 "no permission to write the sync settings");
1086 
1087         syncable = normalizeSyncable(syncable);
1088         final int callingUid = Binder.getCallingUid();
1089         final int callingPid = Binder.getCallingPid();
1090         if (!hasAccountAccess(true, account, callingUid)) {
1091             return;
1092         }
1093         if (!hasAuthorityAccess(providerName, callingUid, userId)) {
1094             return;
1095         }
1096 
1097         final long identityToken = clearCallingIdentity();
1098         try {
1099             getSyncManager().getSyncStorageEngine().setIsSyncable(
1100                     account, userId, providerName, syncable, callingUid, callingPid);
1101         } finally {
1102             restoreCallingIdentity(identityToken);
1103         }
1104     }
1105 
1106     @Override
getMasterSyncAutomatically()1107     public boolean getMasterSyncAutomatically() {
1108         return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
1109     }
1110 
1111     /**
1112      * If the user id supplied is different to the calling user, the caller must hold the
1113      * INTERACT_ACROSS_USERS_FULL permission.
1114      */
1115     @Override
getMasterSyncAutomaticallyAsUser(int userId)1116     public boolean getMasterSyncAutomaticallyAsUser(int userId) {
1117         enforceCrossUserPermission(userId,
1118                 "no permission to read the sync settings for user " + userId);
1119         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
1120                 "no permission to read the sync settings");
1121 
1122         final long identityToken = clearCallingIdentity();
1123         try {
1124             return getSyncManager().getSyncStorageEngine().getMasterSyncAutomatically(userId);
1125         } finally {
1126             restoreCallingIdentity(identityToken);
1127         }
1128     }
1129 
1130     @Override
setMasterSyncAutomatically(boolean flag)1131     public void setMasterSyncAutomatically(boolean flag) {
1132         setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
1133     }
1134 
1135     @Override
setMasterSyncAutomaticallyAsUser(boolean flag, int userId)1136     public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
1137         enforceCrossUserPermission(userId,
1138                 "no permission to set the sync status for user " + userId);
1139         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
1140                 "no permission to write the sync settings");
1141 
1142         final int callingUid = Binder.getCallingUid();
1143         final int callingPid = Binder.getCallingPid();
1144 
1145         final long identityToken = clearCallingIdentity();
1146         try {
1147             getSyncManager().getSyncStorageEngine().setMasterSyncAutomatically(flag, userId,
1148                     getSyncExemptionForCaller(callingUid), callingUid, callingPid);
1149         } finally {
1150             restoreCallingIdentity(identityToken);
1151         }
1152     }
1153 
1154     @android.annotation.EnforcePermission(android.Manifest.permission.READ_SYNC_STATS)
1155     @Override
isSyncActive(Account account, String authority, ComponentName cname)1156     public boolean isSyncActive(Account account, String authority, ComponentName cname) {
1157         isSyncActive_enforcePermission();
1158 
1159         final int callingUid = Binder.getCallingUid();
1160         final int userId = UserHandle.getCallingUserId();
1161         if (!hasAccountAccess(true, account, callingUid)) {
1162             return false;
1163         }
1164         if (!hasAuthorityAccess(authority, callingUid, userId)) {
1165             return false;
1166         }
1167 
1168         final long identityToken = clearCallingIdentity();
1169         try {
1170             return getSyncManager().getSyncStorageEngine().isSyncActive(
1171                     new SyncStorageEngine.EndPoint(account, authority, userId));
1172         } finally {
1173             restoreCallingIdentity(identityToken);
1174         }
1175     }
1176 
1177     @Override
getCurrentSyncs()1178     public List<SyncInfo> getCurrentSyncs() {
1179         return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
1180     }
1181 
1182     /**
1183      * If the user id supplied is different to the calling user, the caller must hold the
1184      * INTERACT_ACROSS_USERS_FULL permission.
1185      */
1186     @Override
getCurrentSyncsAsUser(int userId)1187     public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
1188         enforceCrossUserPermission(userId,
1189                 "no permission to read the sync settings for user " + userId);
1190         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1191                 "no permission to read the sync stats");
1192 
1193         final boolean canAccessAccounts =
1194             mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
1195                 == PackageManager.PERMISSION_GRANTED;
1196         final List<SyncInfo> results;
1197         final int callingUid = Binder.getCallingUid();
1198         final long identityToken = clearCallingIdentity();
1199         try {
1200             results = getSyncManager().getSyncStorageEngine()
1201                     .getCurrentSyncsCopy(userId, canAccessAccounts);
1202         } finally {
1203             restoreCallingIdentity(identityToken);
1204         }
1205         results.removeIf(i -> !hasAuthorityAccess(i.authority, callingUid, userId));
1206         return results;
1207     }
1208 
1209     @Override
getSyncStatus(Account account, String authority, ComponentName cname)1210     public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
1211         return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
1212     }
1213 
1214     /**
1215      * If the user id supplied is different to the calling user, the caller must hold the
1216      * INTERACT_ACROSS_USERS_FULL permission.
1217      */
1218     @Override
1219     @Nullable
getSyncStatusAsUser(Account account, String authority, ComponentName cname, int userId)1220     public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
1221                                               ComponentName cname, int userId) {
1222         if (TextUtils.isEmpty(authority)) {
1223             throw new IllegalArgumentException("Authority must not be empty");
1224         }
1225 
1226         enforceCrossUserPermission(userId,
1227                 "no permission to read the sync stats for user " + userId);
1228         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1229                 "no permission to read the sync stats");
1230 
1231         final int callingUid = Binder.getCallingUid();
1232         if (!hasAccountAccess(true, account, callingUid)) {
1233             return null;
1234         }
1235         if (!hasAuthorityAccess(authority, callingUid, userId)) {
1236             return null;
1237         }
1238 
1239         final long identityToken = clearCallingIdentity();
1240         try {
1241             SyncStorageEngine.EndPoint info;
1242             if (!(account == null || authority == null)) {
1243                 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1244             } else {
1245                 throw new IllegalArgumentException("Must call sync status with valid authority");
1246             }
1247             return getSyncManager().getSyncStorageEngine().getStatusByAuthority(info);
1248         } finally {
1249             restoreCallingIdentity(identityToken);
1250         }
1251     }
1252 
1253     @Override
isSyncPending(Account account, String authority, ComponentName cname)1254     public boolean isSyncPending(Account account, String authority, ComponentName cname) {
1255         return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
1256     }
1257 
1258     @android.annotation.EnforcePermission(android.Manifest.permission.READ_SYNC_STATS)
1259     @Override
isSyncPendingAsUser(Account account, String authority, ComponentName cname, int userId)1260     public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
1261                                        int userId) {
1262         isSyncPendingAsUser_enforcePermission();
1263         enforceCrossUserPermission(userId,
1264                 "no permission to retrieve the sync settings for user " + userId);
1265 
1266         final int callingUid = Binder.getCallingUid();
1267         if (!hasAccountAccess(true, account, callingUid)) {
1268             return false;
1269         }
1270         if (!hasAuthorityAccess(authority, callingUid, userId)) {
1271             return false;
1272         }
1273 
1274         final long identityToken = clearCallingIdentity();
1275         try {
1276             SyncStorageEngine.EndPoint info;
1277             if (!(account == null || authority == null)) {
1278                 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1279             } else {
1280                 throw new IllegalArgumentException("Invalid authority specified");
1281             }
1282             return getSyncManager().getSyncStorageEngine().isSyncPending(info);
1283         } finally {
1284             restoreCallingIdentity(identityToken);
1285         }
1286     }
1287 
1288     @Override
addStatusChangeListener(int mask, ISyncStatusObserver callback)1289     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1290         final int callingUid = Binder.getCallingUid();
1291         final long identityToken = clearCallingIdentity();
1292         try {
1293             if (callback != null) {
1294                 getSyncManager().getSyncStorageEngine().addStatusChangeListener(
1295                         mask, callingUid, callback);
1296             }
1297         } finally {
1298             restoreCallingIdentity(identityToken);
1299         }
1300     }
1301 
1302     @Override
removeStatusChangeListener(ISyncStatusObserver callback)1303     public void removeStatusChangeListener(ISyncStatusObserver callback) {
1304         final long identityToken = clearCallingIdentity();
1305         try {
1306             if (callback != null) {
1307                 getSyncManager().getSyncStorageEngine().removeStatusChangeListener(callback);
1308             }
1309         } finally {
1310             restoreCallingIdentity(identityToken);
1311         }
1312     }
1313 
getProviderPackageName(Uri uri, int userId)1314     private @Nullable String getProviderPackageName(Uri uri, int userId) {
1315         final ProviderInfo pi = mContext.getPackageManager().resolveContentProviderAsUser(
1316                 uri.getAuthority(), 0, userId);
1317         return (pi != null) ? pi.packageName : null;
1318     }
1319 
1320     @GuardedBy("mCache")
findOrCreateCacheLocked(int userId, String providerPackageName)1321     private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1322             String providerPackageName) {
1323         ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1324         if (userCache == null) {
1325             userCache = new ArrayMap<>();
1326             mCache.put(userId, userCache);
1327         }
1328         ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1329         if (packageCache == null) {
1330             packageCache = new ArrayMap<>();
1331             userCache.put(providerPackageName, packageCache);
1332         }
1333         return packageCache;
1334     }
1335 
1336     @GuardedBy("mCache")
invalidateCacheLocked(int userId, String providerPackageName, Uri uri)1337     private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1338         ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1339         if (userCache == null) return;
1340 
1341         ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1342         if (packageCache == null) return;
1343 
1344         if (uri != null) {
1345             for (int i = 0; i < packageCache.size();) {
1346                 final Pair<String, Uri> key = packageCache.keyAt(i);
1347                 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
1348                     if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
1349                     packageCache.removeAt(i);
1350                 } else {
1351                     i++;
1352                 }
1353             }
1354         } else {
1355             if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
1356             packageCache.clear();
1357         }
1358     }
1359 
1360     @Override
1361     @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
putCache(String packageName, Uri key, Bundle value, int userId)1362     public void putCache(String packageName, Uri key, Bundle value, int userId) {
1363         Bundle.setDefusable(value, true);
1364         enforceNonFullCrossUserPermission(userId, TAG);
1365         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1366         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1367                 packageName);
1368 
1369         final String providerPackageName = getProviderPackageName(key, userId);
1370         final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1371 
1372         synchronized (mCache) {
1373             final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1374                     providerPackageName);
1375             if (value != null) {
1376                 cache.put(fullKey, value);
1377             } else {
1378                 cache.remove(fullKey);
1379             }
1380         }
1381     }
1382 
1383     @Override
1384     @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
getCache(String packageName, Uri key, int userId)1385     public Bundle getCache(String packageName, Uri key, int userId) {
1386         enforceNonFullCrossUserPermission(userId, TAG);
1387         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1388         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1389                 packageName);
1390 
1391         final String providerPackageName = getProviderPackageName(key, userId);
1392         final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1393 
1394         synchronized (mCache) {
1395             final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1396                     providerPackageName);
1397             return cache.get(fullKey);
1398         }
1399     }
1400 
handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull, int userId)1401     private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull,
1402             int userId) {
1403         if (userId == UserHandle.USER_CURRENT) {
1404             userId = ActivityManager.getCurrentUser();
1405         }
1406 
1407         if (userId == UserHandle.USER_ALL) {
1408             mContext.enforceCallingOrSelfPermission(
1409                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, "No access to " + uri);
1410         } else if (userId < 0) {
1411             throw new IllegalArgumentException("Invalid user: " + userId);
1412         } else if (userId != UserHandle.getCallingUserId()) {
1413             if (checkUriPermission(uri, pid, uid, modeFlags,
1414                     userId) != PackageManager.PERMISSION_GRANTED) {
1415                 boolean allow = false;
1416                 if (mContext.checkCallingOrSelfPermission(
1417                         Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1418                                 == PackageManager.PERMISSION_GRANTED) {
1419                     allow = true;
1420                 } else if (allowNonFull && mContext.checkCallingOrSelfPermission(
1421                         Manifest.permission.INTERACT_ACROSS_USERS)
1422                                 == PackageManager.PERMISSION_GRANTED) {
1423                     allow = true;
1424                 }
1425                 if (!allow) {
1426                     final String permissions = allowNonFull
1427                             ? (Manifest.permission.INTERACT_ACROSS_USERS_FULL + " or " +
1428                                     Manifest.permission.INTERACT_ACROSS_USERS)
1429                             : Manifest.permission.INTERACT_ACROSS_USERS_FULL;
1430                     throw new SecurityException("No access to " + uri + ": neither user " + uid
1431                             + " nor current process has " + permissions);
1432                 }
1433             }
1434         }
1435 
1436         return userId;
1437     }
1438 
1439     /**
1440      * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1441      * permission, if the userHandle is not for the caller.
1442      *
1443      * @param userHandle the user handle of the user we want to act on behalf of.
1444      * @param message the message to log on security exception.
1445      */
enforceCrossUserPermission(int userHandle, String message)1446     private void enforceCrossUserPermission(int userHandle, String message) {
1447         final int callingUser = UserHandle.getCallingUserId();
1448         if (callingUser != userHandle) {
1449             mContext.enforceCallingOrSelfPermission(
1450                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1451         }
1452     }
1453 
1454     /**
1455      * Checks if the request is from the system or an app that has {@code INTERACT_ACROSS_USERS} or
1456      * {@code INTERACT_ACROSS_USERS_FULL} permission, if the {@code userHandle} is not for the
1457      * caller.
1458      *
1459      * @param userHandle the user handle of the user we want to act on behalf of.
1460      * @param message the message to log on security exception.
1461      */
enforceNonFullCrossUserPermission(int userHandle, String message)1462     private void enforceNonFullCrossUserPermission(int userHandle, String message) {
1463         final int callingUser = UserHandle.getCallingUserId();
1464         if (callingUser == userHandle) {
1465             return;
1466         }
1467 
1468         int interactAcrossUsersState = mContext.checkCallingOrSelfPermission(
1469                 Manifest.permission.INTERACT_ACROSS_USERS);
1470         if (interactAcrossUsersState == PERMISSION_GRANTED) {
1471             return;
1472         }
1473 
1474         mContext.enforceCallingOrSelfPermission(
1475                 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1476     }
1477 
1478     /**
1479      * Checks to see if the given account is accessible by the provided uid.
1480      *
1481      * @param checkCompatFlag whether to check if the ACCOUNT_ACCESS_CHECK_CHANGE_ID flag is enabled
1482      * @param account the account trying to be accessed
1483      * @param uid the uid trying to access the account
1484      * @return {@code true} if the account is accessible by the given uid, {@code false} otherwise
1485      */
hasAccountAccess(boolean checkCompatFlag, Account account, int uid)1486     private boolean hasAccountAccess(boolean checkCompatFlag, Account account, int uid) {
1487         if (account == null) {
1488             // If the account is null, it means to check for all accounts hence skip the check here.
1489             return true;
1490         }
1491         if (checkCompatFlag
1492                 && !CompatChanges.isChangeEnabled(ACCOUNT_ACCESS_CHECK_CHANGE_ID, uid)) {
1493             return true;
1494         }
1495 
1496         final long identityToken = clearCallingIdentity();
1497         try {
1498             return mAccountManagerInternal.hasAccountAccess(account, uid);
1499         } finally {
1500             restoreCallingIdentity(identityToken);
1501         }
1502     }
1503 
1504     /**
1505      * Checks to see if the given authority is accessible by the caller.
1506      *
1507      * @param authority the authority to be accessed
1508      * @param uid the uid trying to access the authority
1509      * @param userId the user id for which to access the authority
1510      * @return {@code true} if the authority is accessible by the caller, {@code false} otherwise
1511      */
hasAuthorityAccess(@ullable String authority, int uid, @UserIdInt int userId)1512     private boolean hasAuthorityAccess(@Nullable String authority, int uid, @UserIdInt int userId) {
1513         if (TextUtils.isEmpty(authority)) {
1514             return true;
1515         }
1516         if (!CompatChanges.isChangeEnabled(AUTHORITY_ACCESS_CHECK_CHANGE_ID, uid)) {
1517             return true;
1518         }
1519         // Since #getSyncAdapterPackagesForAuthorityAsUser would filter out the packages
1520         // that aren't visible to the callers, using this to check if the given authority
1521         // is accessible by the callers.
1522         final String[] syncAdapterPackages =
1523                 getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
1524         return !ArrayUtils.isEmpty(syncAdapterPackages);
1525     }
1526 
1527 
normalizeSyncable(int syncable)1528     private static int normalizeSyncable(int syncable) {
1529         if (syncable > 0) {
1530             return SyncStorageEngine.AuthorityInfo.SYNCABLE;
1531         } else if (syncable == 0) {
1532             return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1533         }
1534         return SyncStorageEngine.AuthorityInfo.UNDEFINED;
1535     }
1536 
validateExtras(int callingUid, Bundle extras)1537     private void validateExtras(int callingUid, Bundle extras) {
1538         if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG)) {
1539             switch (callingUid) {
1540                 case Process.ROOT_UID:
1541                 case Process.SHELL_UID:
1542                 case Process.SYSTEM_UID:
1543                     break; // Okay
1544                 default:
1545                     final String msg = "Invalid extras specified.";
1546                     Log.w(TAG, msg + " requestsync -f/-F needs to run on 'adb shell'");
1547                     throw new SecurityException(msg);
1548             }
1549         }
1550     }
1551 
1552     @SyncExemption
getSyncExemptionForCaller(int callingUid)1553     private int getSyncExemptionForCaller(int callingUid) {
1554         return getSyncExemptionAndCleanUpExtrasForCaller(callingUid, null);
1555     }
1556 
1557     @SyncExemption
getSyncExemptionAndCleanUpExtrasForCaller(int callingUid, Bundle extras)1558     private int getSyncExemptionAndCleanUpExtrasForCaller(int callingUid, Bundle extras) {
1559         if (extras != null) {
1560             final int exemption =
1561                     extras.getInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, -1);
1562 
1563             // Need to remove the virtual extra.
1564             extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG);
1565             if (exemption != -1) {
1566                 return exemption;
1567             }
1568         }
1569         final ActivityManagerInternal ami =
1570                 LocalServices.getService(ActivityManagerInternal.class);
1571         if (ami == null) {
1572             return ContentResolver.SYNC_EXEMPTION_NONE;
1573         }
1574         final int procState = ami.getUidProcessState(callingUid);
1575         final boolean isUidActive = ami.isUidActive(callingUid);
1576 
1577         // Providers bound by a TOP app will get PROCESS_STATE_BOUND_TOP, so include those as well
1578         if (procState <= ActivityManager.PROCESS_STATE_TOP
1579                 || procState == ActivityManager.PROCESS_STATE_BOUND_TOP) {
1580             return ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP;
1581         }
1582         if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || isUidActive) {
1583             FrameworkStatsLog.write(FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED,
1584                     callingUid, getProcStateForStatsd(procState), isUidActive,
1585                     getRestrictionLevelForStatsd(ami.getRestrictionLevel(callingUid)));
1586             return ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET;
1587         }
1588         return ContentResolver.SYNC_EXEMPTION_NONE;
1589     }
1590 
getProcStateForStatsd(int procState)1591     private int getProcStateForStatsd(int procState) {
1592         switch (procState) {
1593             case ActivityManager.PROCESS_STATE_UNKNOWN:
1594                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__UNKNOWN;
1595             case ActivityManager.PROCESS_STATE_PERSISTENT:
1596                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__PERSISTENT;
1597             case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
1598                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__PERSISTENT_UI;
1599             case ActivityManager.PROCESS_STATE_TOP:
1600                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__TOP;
1601             case ActivityManager.PROCESS_STATE_BOUND_TOP:
1602                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__BOUND_TOP;
1603             case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
1604                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__FOREGROUND_SERVICE;
1605             case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
1606                 return FrameworkStatsLog
1607                         .SYNC_EXEMPTION_OCCURRED__PROC_STATE__BOUND_FOREGROUND_SERVICE;
1608             case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
1609                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__IMPORTANT_FOREGROUND;
1610             case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
1611                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__IMPORTANT_BACKGROUND;
1612             case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
1613                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__TRANSIENT_BACKGROUND;
1614             case ActivityManager.PROCESS_STATE_BACKUP:
1615                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__BACKUP;
1616             case ActivityManager.PROCESS_STATE_SERVICE:
1617                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__SERVICE;
1618             case ActivityManager.PROCESS_STATE_RECEIVER:
1619                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__RECEIVER;
1620             case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
1621                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__TOP_SLEEPING;
1622             case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
1623                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__HEAVY_WEIGHT;
1624             case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
1625                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__LAST_ACTIVITY;
1626             case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
1627                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__CACHED_ACTIVITY;
1628             case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
1629                 return FrameworkStatsLog
1630                         .SYNC_EXEMPTION_OCCURRED__PROC_STATE__CACHED_ACTIVITY_CLIENT;
1631             case ActivityManager.PROCESS_STATE_CACHED_RECENT:
1632                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__CACHED_RECENT;
1633             case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
1634                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__CACHED_EMPTY;
1635             default:
1636                 return FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED__PROC_STATE__UNKNOWN;
1637         }
1638     }
1639 
getRestrictionLevelForStatsd(@estrictionLevel int level)1640     private int getRestrictionLevelForStatsd(@RestrictionLevel int level) {
1641         switch (level) {
1642             case ActivityManager.RESTRICTION_LEVEL_UNKNOWN:
1643                 return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
1644             case ActivityManager.RESTRICTION_LEVEL_UNRESTRICTED:
1645                 return AppBackgroundRestrictionsInfo.LEVEL_UNRESTRICTED;
1646             case ActivityManager.RESTRICTION_LEVEL_EXEMPTED:
1647                 return AppBackgroundRestrictionsInfo.LEVEL_EXEMPTED;
1648             case ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
1649                 return AppBackgroundRestrictionsInfo.LEVEL_ADAPTIVE_BUCKET;
1650             case ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET:
1651                 return AppBackgroundRestrictionsInfo.LEVEL_RESTRICTED_BUCKET;
1652             case ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
1653                 return AppBackgroundRestrictionsInfo.LEVEL_BACKGROUND_RESTRICTED;
1654             case ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED:
1655                 return AppBackgroundRestrictionsInfo.LEVEL_HIBERNATION;
1656             default:
1657                 return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
1658         }
1659     }
1660 
1661     /** {@hide} */
1662     @VisibleForTesting
1663     public static final class ObserverNode {
1664         private class ObserverEntry implements IBinder.DeathRecipient {
1665             public final IContentObserver observer;
1666             public final int uid;
1667             public final int pid;
1668             public final boolean notifyForDescendants;
1669             private final int userHandle;
1670             private final Object observersLock;
1671 
ObserverEntry(IContentObserver o, boolean n, Object observersLock, int _uid, int _pid, int _userHandle, Uri uri)1672             public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
1673                                  int _uid, int _pid, int _userHandle, Uri uri) {
1674                 this.observersLock = observersLock;
1675                 observer = o;
1676                 uid = _uid;
1677                 pid = _pid;
1678                 userHandle = _userHandle;
1679                 notifyForDescendants = n;
1680 
1681                 final int entries = sObserverDeathDispatcher.linkToDeath(observer, this);
1682                 if (entries == -1) {
1683                     binderDied();
1684                 } else if (entries == TOO_MANY_OBSERVERS_THRESHOLD) {
1685                     boolean alreadyDetected;
1686 
1687                     synchronized (sObserverLeakDetectedUid) {
1688                         alreadyDetected = sObserverLeakDetectedUid.contains(uid);
1689                         if (!alreadyDetected) {
1690                             sObserverLeakDetectedUid.add(uid);
1691                         }
1692                     }
1693                     if (!alreadyDetected) {
1694                         String caller = null;
1695                         try {
1696                             caller = ArrayUtils.firstOrNull(AppGlobals.getPackageManager()
1697                                     .getPackagesForUid(uid));
1698                         } catch (RemoteException ignore) {
1699                         }
1700                         Slog.wtf(TAG, "Observer registered too many times. Leak? cpid=" + pid
1701                                 + " cuid=" + uid
1702                                 + " cpkg=" + caller
1703                                 + " url=" + uri);
1704                     }
1705                 }
1706 
1707             }
1708 
1709             @Override
binderDied()1710             public void binderDied() {
1711                 synchronized (observersLock) {
1712                     removeObserverLocked(observer);
1713                 }
1714             }
1715 
dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, String name, String prefix, SparseIntArray pidCounts)1716             public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1717                                    String name, String prefix, SparseIntArray pidCounts) {
1718                 pidCounts.put(pid, pidCounts.get(pid)+1);
1719                 pw.print(prefix); pw.print(name); pw.print(": pid=");
1720                 pw.print(pid); pw.print(" uid=");
1721                 pw.print(uid); pw.print(" user=");
1722                 pw.print(userHandle); pw.print(" target=");
1723                 pw.println(Integer.toHexString(System.identityHashCode(
1724                         observer != null ? observer.asBinder() : null)));
1725             }
1726         }
1727 
1728         private String mName;
1729         private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1730         private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1731 
ObserverNode(String name)1732         public ObserverNode(String name) {
1733             mName = name;
1734         }
1735 
dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, String name, String prefix, int[] counts, SparseIntArray pidCounts)1736         public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1737                                String name, String prefix, int[] counts, SparseIntArray pidCounts) {
1738             String innerName = null;
1739             if (mObservers.size() > 0) {
1740                 if ("".equals(name)) {
1741                     innerName = mName;
1742                 } else {
1743                     innerName = name + "/" + mName;
1744                 }
1745                 for (int i=0; i<mObservers.size(); i++) {
1746                     counts[1]++;
1747                     mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1748                             pidCounts);
1749                 }
1750             }
1751             if (mChildren.size() > 0) {
1752                 if (innerName == null) {
1753                     if ("".equals(name)) {
1754                         innerName = mName;
1755                     } else {
1756                         innerName = name + "/" + mName;
1757                     }
1758                 }
1759                 for (int i=0; i<mChildren.size(); i++) {
1760                     counts[0]++;
1761                     mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1762                             counts, pidCounts);
1763                 }
1764             }
1765         }
1766 
getUriSegment(Uri uri, int index)1767         public static String getUriSegment(Uri uri, int index) {
1768             if (uri != null) {
1769                 if (index == 0) {
1770                     return uri.getAuthority();
1771                 } else {
1772                     return uri.getPathSegments().get(index - 1);
1773                 }
1774             } else {
1775                 return null;
1776             }
1777         }
1778 
countUriSegments(Uri uri)1779         public static int countUriSegments(Uri uri) {
1780             if (uri == null) {
1781                 return 0;
1782             }
1783             return uri.getPathSegments().size() + 1;
1784         }
1785 
1786         // Invariant:  userHandle is either a hard user number or is USER_ALL
addObserverLocked(Uri uri, IContentObserver observer, boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle)1787         public void addObserverLocked(Uri uri, IContentObserver observer,
1788                                       boolean notifyForDescendants, Object observersLock,
1789                                       int uid, int pid, int userHandle) {
1790             addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1791                     uid, pid, userHandle);
1792         }
1793 
addObserverLocked(Uri uri, int index, IContentObserver observer, boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle)1794         private void addObserverLocked(Uri uri, int index, IContentObserver observer,
1795                                        boolean notifyForDescendants, Object observersLock,
1796                                        int uid, int pid, int userHandle) {
1797             // If this is the leaf node add the observer
1798             if (index == countUriSegments(uri)) {
1799                 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1800                         uid, pid, userHandle, uri));
1801                 return;
1802             }
1803 
1804             // Look to see if the proper child already exists
1805             String segment = getUriSegment(uri, index);
1806             if (segment == null) {
1807                 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1808             }
1809             int N = mChildren.size();
1810             for (int i = 0; i < N; i++) {
1811                 ObserverNode node = mChildren.get(i);
1812                 if (node.mName.equals(segment)) {
1813                     node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1814                             observersLock, uid, pid, userHandle);
1815                     return;
1816                 }
1817             }
1818 
1819             // No child found, create one
1820             ObserverNode node = new ObserverNode(segment);
1821             mChildren.add(node);
1822             node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1823                     observersLock, uid, pid, userHandle);
1824         }
1825 
removeObserverLocked(IContentObserver observer)1826         public boolean removeObserverLocked(IContentObserver observer) {
1827             int size = mChildren.size();
1828             for (int i = 0; i < size; i++) {
1829                 boolean empty = mChildren.get(i).removeObserverLocked(observer);
1830                 if (empty) {
1831                     mChildren.remove(i);
1832                     i--;
1833                     size--;
1834                 }
1835             }
1836 
1837             IBinder observerBinder = observer.asBinder();
1838             size = mObservers.size();
1839             for (int i = 0; i < size; i++) {
1840                 ObserverEntry entry = mObservers.get(i);
1841                 if (entry.observer.asBinder() == observerBinder) {
1842                     mObservers.remove(i);
1843                     // We no longer need to listen for death notifications. Remove it.
1844                     sObserverDeathDispatcher.unlinkToDeath(observer, entry);
1845                     break;
1846                 }
1847             }
1848 
1849             if (mChildren.size() == 0 && mObservers.size() == 0) {
1850                 return true;
1851             }
1852             return false;
1853         }
1854 
collectMyObserversLocked(Uri uri, boolean leaf, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int targetUserHandle, ObserverCollector collector)1855         private void collectMyObserversLocked(Uri uri, boolean leaf, IContentObserver observer,
1856                                               boolean observerWantsSelfNotifications, int flags,
1857                                               int targetUserHandle, ObserverCollector collector) {
1858             int N = mObservers.size();
1859             IBinder observerBinder = observer == null ? null : observer.asBinder();
1860             for (int i = 0; i < N; i++) {
1861                 ObserverEntry entry = mObservers.get(i);
1862 
1863                 // Don't notify the observer if it sent the notification and isn't interested
1864                 // in self notifications
1865                 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1866                 if (selfChange && !observerWantsSelfNotifications) {
1867                     continue;
1868                 }
1869 
1870                 // Does this observer match the target user?
1871                 if (targetUserHandle == UserHandle.USER_ALL
1872                         || entry.userHandle == UserHandle.USER_ALL
1873                         || targetUserHandle == entry.userHandle) {
1874                     // Make sure the observer is interested in the notification
1875                     if (leaf) {
1876                         // If we are at the leaf: we always report, unless the sender has asked
1877                         // to skip observers that are notifying for descendants (since they will
1878                         // be sending another more specific URI for them).
1879                         if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1880                                 && entry.notifyForDescendants) {
1881                             if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1882                                     + ": skip notify for descendants");
1883                             continue;
1884                         }
1885                     } else {
1886                         // If we are not at the leaf: we report if the observer says it wants
1887                         // to be notified for all descendants.
1888                         if (!entry.notifyForDescendants) {
1889                             if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1890                                     + ": not monitor descendants");
1891                             continue;
1892                         }
1893                     }
1894                     if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1895                             + " flags=" + Integer.toHexString(flags)
1896                             + " desc=" + entry.notifyForDescendants);
1897                     collector.collect(entry.observer, entry.uid, selfChange, uri, flags,
1898                             targetUserHandle);
1899                 }
1900             }
1901         }
1902 
1903         @VisibleForTesting
collectObserversLocked(Uri uri, int index, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int targetUserHandle, ObserverCollector collector)1904         public void collectObserversLocked(Uri uri, int index,
1905                 IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
1906                 int targetUserHandle, ObserverCollector collector) {
1907             collectObserversLocked(uri, countUriSegments(uri), index, observer,
1908                     observerWantsSelfNotifications, flags, targetUserHandle, collector);
1909         }
1910 
1911         /**
1912          * targetUserHandle is either a hard user handle or is USER_ALL
1913          */
collectObserversLocked(Uri uri, int segmentCount, int index, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int targetUserHandle, ObserverCollector collector)1914         public void collectObserversLocked(Uri uri, int segmentCount, int index,
1915                 IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
1916                 int targetUserHandle, ObserverCollector collector) {
1917             String segment = null;
1918             if (index >= segmentCount) {
1919                 // This is the leaf node, notify all observers
1920                 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
1921                 collectMyObserversLocked(uri, true, observer, observerWantsSelfNotifications,
1922                         flags, targetUserHandle, collector);
1923             } else if (index < segmentCount){
1924                 segment = getUriSegment(uri, index);
1925                 if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1926                         + segment);
1927                 // Notify any observers at this level who are interested in descendants
1928                 collectMyObserversLocked(uri, false, observer, observerWantsSelfNotifications,
1929                         flags, targetUserHandle, collector);
1930             }
1931 
1932             int N = mChildren.size();
1933             for (int i = 0; i < N; i++) {
1934                 ObserverNode node = mChildren.get(i);
1935                 if (segment == null || node.mName.equals(segment)) {
1936                     // We found the child,
1937                     node.collectObserversLocked(uri, segmentCount, index + 1, observer,
1938                             observerWantsSelfNotifications, flags, targetUserHandle, collector);
1939                     if (segment != null) {
1940                         break;
1941                     }
1942                 }
1943             }
1944         }
1945     }
1946 
enforceShell(String method)1947     private void enforceShell(String method) {
1948         final int callingUid = Binder.getCallingUid();
1949         if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
1950             throw new SecurityException("Non-shell user attempted to call " + method);
1951         }
1952     }
1953 
1954     @Override
resetTodayStats()1955     public void resetTodayStats() {
1956         enforceShell("resetTodayStats");
1957 
1958         if (mSyncManager != null) {
1959             final long token = Binder.clearCallingIdentity();
1960             try {
1961                 mSyncManager.resetTodayStats();
1962             } finally {
1963                 Binder.restoreCallingIdentity(token);
1964             }
1965         }
1966     }
1967 
1968     @Override
onDbCorruption(String tag, String message, String stacktrace)1969     public void onDbCorruption(String tag, String message, String stacktrace) {
1970         Slog.e(tag, message);
1971         Slog.e(tag, "at " + stacktrace);
1972 
1973         // TODO: Figure out a better way to report it. b/117886381
1974         Slog.wtf(tag, message);
1975     }
1976 
1977     @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1978     public void onShellCommand(FileDescriptor in, FileDescriptor out,
1979             FileDescriptor err, String[] args, ShellCallback callback,
1980             ResultReceiver resultReceiver) {
1981         (new ContentShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
1982     }
1983 }
1984