2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.content;
19 import android.Manifest;
20 import android.accounts.Account;
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.app.ActivityManagerInternal;
24 import android.app.AppOpsManager;
25 import android.app.job.JobInfo;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.ContentResolver;
29 import android.content.Context;
30 import android.content.IContentService;
31 import android.content.ISyncStatusObserver;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.PeriodicSync;
35 import android.content.SyncAdapterType;
36 import android.content.SyncInfo;
37 import android.content.SyncRequest;
38 import android.content.SyncStatusInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManagerInternal;
41 import android.content.pm.ProviderInfo;
42 import android.database.IContentObserver;
43 import android.database.sqlite.SQLiteException;
44 import android.net.Uri;
45 import android.os.Binder;
46 import android.os.Build;
47 import android.os.Bundle;
48 import android.os.FactoryTest;
49 import android.os.IBinder;
50 import android.os.Parcel;
51 import android.os.RemoteException;
52 import android.os.SystemProperties;
53 import android.os.UserHandle;
54 import android.text.TextUtils;
55 import android.util.ArrayMap;
56 import android.util.Log;
57 import android.util.Pair;
58 import android.util.Slog;
59 import android.util.SparseArray;
60 import android.util.SparseIntArray;
62 import com.android.internal.annotations.GuardedBy;
63 import com.android.internal.util.ArrayUtils;
64 import com.android.internal.util.DumpUtils;
65 import com.android.internal.util.IndentingPrintWriter;
66 import com.android.server.LocalServices;
67 import com.android.server.SystemService;
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 import java.util.ArrayList;
72 import java.util.Collections;
73 import java.util.Comparator;
74 import java.util.List;
79 public final class ContentService extends IContentService.Stub {
80 static final String TAG = "ContentService";
81 static final boolean DEBUG = false;
83 public static class Lifecycle extends SystemService {
84 private ContentService mService;
86 public Lifecycle(Context context) {
91 public void onStart() {
92 final boolean factoryTest = (FactoryTest
93 .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
94 mService = new ContentService(getContext(), factoryTest);
95 publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
99 public void onBootPhase(int phase) {
100 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
101 mService.systemReady();
107 public void onStartUser(int userHandle) {
108 mService.onStartUser(userHandle);
112 public void onUnlockUser(int userHandle) {
113 mService.onUnlockUser(userHandle);
117 public void onStopUser(int userHandle) {
118 mService.onStopUser(userHandle);
122 public void onCleanupUser(int userHandle) {
123 synchronized (mService.mCache) {
124 mService.mCache.remove(userHandle);
129 private Context mContext;
130 private boolean mFactoryTest;
132 private final ObserverNode mRootNode = new ObserverNode("");
134 private SyncManager mSyncManager = null;
135 private final Object mSyncManagerLock = new Object();
138 * Map from userId to providerPackageName to [clientPackageName, uri] to
139 * value. This structure is carefully optimized to keep invalidation logic
140 * as cheap as possible.
143 private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
144 mCache = new SparseArray<>();
146 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
148 public void onReceive(Context context, Intent intent) {
149 synchronized (mCache) {
150 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
153 final Uri data = intent.getData();
155 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
156 UserHandle.USER_NULL);
157 final String packageName = data.getSchemeSpecificPart();
158 invalidateCacheLocked(userId, packageName, null);
165 private SyncManager getSyncManager() {
166 if (SystemProperties.getBoolean("config.disable_network", false)) {
170 synchronized(mSyncManagerLock) {
172 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
173 if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
174 } catch (SQLiteException e) {
175 Log.e(TAG, "Can't create SyncManager", e);
181 void onStartUser(int userHandle) {
182 if (mSyncManager != null) mSyncManager.onStartUser(userHandle);
185 void onUnlockUser(int userHandle) {
186 if (mSyncManager != null) mSyncManager.onUnlockUser(userHandle);
189 void onStopUser(int userHandle) {
190 if (mSyncManager != null) mSyncManager.onStopUser(userHandle);
194 protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
195 if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw_)) return;
196 final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
198 final boolean dumpAll = ArrayUtils.contains(args, "-a");
200 // This makes it so that future permission checks will be in the context of this
201 // process rather than the caller's process. We will restore this before returning.
202 final long identityToken = clearCallingIdentity();
204 if (mSyncManager == null) {
205 pw.println("No SyncManager created! (Disk full?)");
207 mSyncManager.dump(fd, pw, dumpAll);
210 pw.println("Observer tree:");
211 synchronized (mRootNode) {
212 int[] counts = new int[2];
213 final SparseIntArray pidCounts = new SparseIntArray();
214 mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
216 ArrayList<Integer> sorted = new ArrayList<Integer>();
217 for (int i=0; i<pidCounts.size(); i++) {
218 sorted.add(pidCounts.keyAt(i));
220 Collections.sort(sorted, new Comparator<Integer>() {
222 public int compare(Integer lhs, Integer rhs) {
223 int lc = pidCounts.get(lhs);
224 int rc = pidCounts.get(rhs);
227 } else if (lc > rc) {
234 for (int i=0; i<sorted.size(); i++) {
235 int pid = sorted.get(i);
236 pw.print(" pid "); pw.print(pid); pw.print(": ");
237 pw.print(pidCounts.get(pid)); pw.println(" observers");
240 pw.print(" Total number of nodes: "); pw.println(counts[0]);
241 pw.print(" Total number of observers: "); pw.println(counts[1]);
244 synchronized (mCache) {
246 pw.println("Cached content:");
248 for (int i = 0; i < mCache.size(); i++) {
249 pw.println("User " + mCache.keyAt(i) + ":");
251 pw.println(mCache.valueAt(i));
257 restoreCallingIdentity(identityToken);
262 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
263 throws RemoteException {
265 return super.onTransact(code, data, reply, flags);
266 } catch (RuntimeException e) {
267 // The content service only throws security exceptions, so let's
269 if (!(e instanceof SecurityException)) {
270 Slog.wtf(TAG, "Content Service Crash", e);
276 /*package*/ ContentService(Context context, boolean factoryTest) {
278 mFactoryTest = factoryTest;
280 // Let the package manager query for the sync adapters for a given authority
281 // as we grant default permissions to sync adapters for specific authorities.
282 PackageManagerInternal packageManagerInternal = LocalServices.getService(
283 PackageManagerInternal.class);
284 packageManagerInternal.setSyncAdapterPackagesprovider(
285 new PackageManagerInternal.SyncAdapterPackagesProvider() {
287 public String[] getPackages(String authority, int userId) {
288 return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
292 final IntentFilter packageFilter = new IntentFilter();
293 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
294 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
295 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
296 packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
297 packageFilter.addDataScheme("package");
298 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
299 packageFilter, null, null);
301 final IntentFilter localeFilter = new IntentFilter();
302 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
303 mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
304 localeFilter, null, null);
312 * Register a content observer tied to a specific user's view of the provider.
313 * @param userHandle the user whose view of the provider is to be observed. May be
314 * the calling user without requiring any permission, otherwise the caller needs to
315 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
316 * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
320 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
321 IContentObserver observer, int userHandle, int targetSdkVersion) {
322 if (observer == null || uri == null) {
323 throw new IllegalArgumentException("You must pass a valid uri and observer");
326 final int uid = Binder.getCallingUid();
327 final int pid = Binder.getCallingPid();
329 userHandle = handleIncomingUser(uri, pid, uid,
330 Intent.FLAG_GRANT_READ_URI_PERMISSION, true, userHandle);
332 final String msg = LocalServices.getService(ActivityManagerInternal.class)
333 .checkContentProviderAccess(uri.getAuthority(), userHandle);
335 if (targetSdkVersion >= Build.VERSION_CODES.O) {
336 throw new SecurityException(msg);
338 if (msg.startsWith("Failed to find provider")) {
339 // Sigh, we need to quietly let apps targeting older API
340 // levels notify on non-existent providers.
342 Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
348 synchronized (mRootNode) {
349 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
350 uid, pid, userHandle);
351 if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
352 " with notifyForDescendants " + notifyForDescendants);
356 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
357 IContentObserver observer) {
358 registerContentObserver(uri, notifyForDescendants, observer,
359 UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT);
363 public void unregisterContentObserver(IContentObserver observer) {
364 if (observer == null) {
365 throw new IllegalArgumentException("You must pass a valid observer");
367 synchronized (mRootNode) {
368 mRootNode.removeObserverLocked(observer);
369 if (false) Log.v(TAG, "Unregistered observer " + observer);
374 * Notify observers of a particular user's view of the provider.
375 * @param userHandle the user whose view of the provider is to be notified. May be
376 * the calling user without requiring any permission, otherwise the caller needs to
377 * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
378 * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
382 public void notifyChange(Uri uri, IContentObserver observer,
383 boolean observerWantsSelfNotifications, int flags, int userHandle,
384 int targetSdkVersion) {
385 if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
386 + " from observer " + observer + ", flags " + Integer.toHexString(flags));
389 throw new NullPointerException("Uri must not be null");
392 final int uid = Binder.getCallingUid();
393 final int pid = Binder.getCallingPid();
394 final int callingUserHandle = UserHandle.getCallingUserId();
396 userHandle = handleIncomingUser(uri, pid, uid,
397 Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userHandle);
399 final String msg = LocalServices.getService(ActivityManagerInternal.class)
400 .checkContentProviderAccess(uri.getAuthority(), userHandle);
402 if (targetSdkVersion >= Build.VERSION_CODES.O) {
403 throw new SecurityException(msg);
405 if (msg.startsWith("Failed to find provider")) {
406 // Sigh, we need to quietly let apps targeting older API
407 // levels notify on non-existent providers.
409 Log.w(TAG, "Ignoring notify for " + uri + " from " + uid + ": " + msg);
415 // This makes it so that future permission checks will be in the context of this
416 // process rather than the caller's process. We will restore this before returning.
417 long identityToken = clearCallingIdentity();
419 ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
420 synchronized (mRootNode) {
421 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
422 flags, userHandle, calls);
424 final int numCalls = calls.size();
425 for (int i=0; i<numCalls; i++) {
426 ObserverCall oc = calls.get(i);
428 oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
429 if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
431 } catch (RemoteException ex) {
432 synchronized (mRootNode) {
433 Log.w(TAG, "Found dead observer, removing");
434 IBinder binder = oc.mObserver.asBinder();
435 final ArrayList<ObserverNode.ObserverEntry> list
436 = oc.mNode.mObservers;
437 int numList = list.size();
438 for (int j=0; j<numList; j++) {
439 ObserverNode.ObserverEntry oe = list.get(j);
440 if (oe.observer.asBinder() == binder) {
449 if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
450 SyncManager syncManager = getSyncManager();
451 if (syncManager != null) {
452 syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
457 synchronized (mCache) {
458 final String providerPackageName = getProviderPackageName(uri);
459 invalidateCacheLocked(userHandle, providerPackageName, uri);
462 restoreCallingIdentity(identityToken);
466 private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
468 return ActivityManager.getService().checkUriPermission(
469 uri, pid, uid, modeFlags, userHandle, null);
470 } catch (RemoteException e) {
471 return PackageManager.PERMISSION_DENIED;
475 public void notifyChange(Uri uri, IContentObserver observer,
476 boolean observerWantsSelfNotifications, boolean syncToNetwork) {
477 notifyChange(uri, observer, observerWantsSelfNotifications,
478 syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
479 UserHandle.getCallingUserId(), Build.VERSION_CODES.CUR_DEVELOPMENT);
483 * Hide this class since it is not part of api,
484 * but current unittest framework requires it to be public
488 public static final class ObserverCall {
489 final ObserverNode mNode;
490 final IContentObserver mObserver;
491 final boolean mSelfChange;
492 final int mObserverUserId;
494 ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange, int observerUserId) {
496 mObserver = observer;
497 mSelfChange = selfChange;
498 mObserverUserId = observerUserId;
503 public void requestSync(Account account, String authority, Bundle extras) {
504 Bundle.setDefusable(extras, true);
505 ContentResolver.validateSyncExtrasBundle(extras);
506 int userId = UserHandle.getCallingUserId();
507 int uId = Binder.getCallingUid();
509 // This makes it so that future permission checks will be in the context of this
510 // process rather than the caller's process. We will restore this before returning.
511 long identityToken = clearCallingIdentity();
513 SyncManager syncManager = getSyncManager();
514 if (syncManager != null) {
515 syncManager.scheduleSync(account, userId, uId, authority, extras,
516 SyncStorageEngine.AuthorityInfo.UNDEFINED);
519 restoreCallingIdentity(identityToken);
524 * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
526 * periodic OR one-off sync.
528 * anonymous OR provider sync.
529 * Depending on the request, we enqueue to suit in the SyncManager.
530 * @param request The request object. Validation of this object is done by its builder.
533 public void sync(SyncRequest request) {
534 syncAsUser(request, UserHandle.getCallingUserId());
537 private long clampPeriod(long period) {
538 long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
539 if (period < minPeriod) {
540 Slog.w(TAG, "Requested poll frequency of " + period
541 + " seconds being rounded up to " + minPeriod + "s.");
548 * If the user id supplied is different to the calling user, the caller must hold the
549 * INTERACT_ACROSS_USERS_FULL permission.
552 public void syncAsUser(SyncRequest request, int userId) {
553 enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
554 int callerUid = Binder.getCallingUid();
555 // This makes it so that future permission checks will be in the context of this
556 // process rather than the caller's process. We will restore this before returning.
557 long identityToken = clearCallingIdentity();
559 SyncManager syncManager = getSyncManager();
560 if (syncManager == null) {
564 Bundle extras = request.getBundle();
565 long flextime = request.getSyncFlexTime();
566 long runAtTime = request.getSyncRunTime();
567 if (request.isPeriodic()) {
568 mContext.enforceCallingOrSelfPermission(
569 Manifest.permission.WRITE_SYNC_SETTINGS,
570 "no permission to write the sync settings");
571 SyncStorageEngine.EndPoint info;
572 info = new SyncStorageEngine.EndPoint(
573 request.getAccount(), request.getProvider(), userId);
575 runAtTime = clampPeriod(runAtTime);
576 // Schedule periodic sync.
577 getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
580 syncManager.scheduleSync(
581 request.getAccount(), userId, callerUid, request.getProvider(), extras,
582 SyncStorageEngine.AuthorityInfo.UNDEFINED);
585 restoreCallingIdentity(identityToken);
590 * Clear all scheduled sync operations that match the uri and cancel the active sync
591 * if they match the authority and account, if they are present.
593 * @param account filter the pending and active syncs to cancel using this account, or null.
594 * @param authority filter the pending and active syncs to cancel using this authority, or
596 * @param cname cancel syncs running on this service, or null for provider/account.
599 public void cancelSync(Account account, String authority, ComponentName cname) {
600 cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
604 * Clear all scheduled sync operations that match the uri and cancel the active sync
605 * if they match the authority and account, if they are present.
607 * <p> If the user id supplied is different to the calling user, the caller must hold the
608 * INTERACT_ACROSS_USERS_FULL permission.
610 * @param account filter the pending and active syncs to cancel using this account, or null.
611 * @param authority filter the pending and active syncs to cancel using this authority, or
613 * @param userId the user id for which to cancel sync operations.
614 * @param cname cancel syncs running on this service, or null for provider/account.
617 public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
619 if (authority != null && authority.length() == 0) {
620 throw new IllegalArgumentException("Authority must be non-empty");
622 enforceCrossUserPermission(userId,
623 "no permission to modify the sync settings for user " + userId);
624 // This makes it so that future permission checks will be in the context of this
625 // process rather than the caller's process. We will restore this before returning.
626 long identityToken = clearCallingIdentity();
628 Slog.e(TAG, "cname not null.");
632 SyncManager syncManager = getSyncManager();
633 if (syncManager != null) {
634 SyncStorageEngine.EndPoint info;
635 info = new SyncStorageEngine.EndPoint(account, authority, userId);
636 syncManager.clearScheduledSyncOperations(info);
637 syncManager.cancelActiveSync(info, null /* all syncs for this adapter */, "API");
640 restoreCallingIdentity(identityToken);
645 public void cancelRequest(SyncRequest request) {
646 SyncManager syncManager = getSyncManager();
647 if (syncManager == null) return;
648 int userId = UserHandle.getCallingUserId();
650 long identityToken = clearCallingIdentity();
652 SyncStorageEngine.EndPoint info;
653 Bundle extras = new Bundle(request.getBundle());
654 Account account = request.getAccount();
655 String provider = request.getProvider();
656 info = new SyncStorageEngine.EndPoint(account, provider, userId);
657 if (request.isPeriodic()) {
658 // Remove periodic sync.
659 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
660 "no permission to write the sync settings");
661 getSyncManager().removePeriodicSync(info, extras);
663 // Cancel active syncs and clear pending syncs from the queue.
664 syncManager.cancelScheduledSyncOperation(info, extras);
665 syncManager.cancelActiveSync(info, extras, "API");
667 restoreCallingIdentity(identityToken);
672 * Get information about the SyncAdapters that are known to the system.
673 * @return an array of SyncAdapters that have registered with the system
676 public SyncAdapterType[] getSyncAdapterTypes() {
677 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
681 * Get information about the SyncAdapters that are known to the system for a particular user.
683 * <p> If the user id supplied is different to the calling user, the caller must hold the
684 * INTERACT_ACROSS_USERS_FULL permission.
686 * @return an array of SyncAdapters that have registered with the system
689 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
690 enforceCrossUserPermission(userId,
691 "no permission to read sync settings for user " + userId);
692 // This makes it so that future permission checks will be in the context of this
693 // process rather than the caller's process. We will restore this before returning.
694 final long identityToken = clearCallingIdentity();
696 SyncManager syncManager = getSyncManager();
697 return syncManager.getSyncAdapterTypes(userId);
699 restoreCallingIdentity(identityToken);
704 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
705 enforceCrossUserPermission(userId,
706 "no permission to read sync settings for user " + userId);
707 // This makes it so that future permission checks will be in the context of this
708 // process rather than the caller's process. We will restore this before returning.
709 final long identityToken = clearCallingIdentity();
711 SyncManager syncManager = getSyncManager();
712 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
714 restoreCallingIdentity(identityToken);
719 public boolean getSyncAutomatically(Account account, String providerName) {
720 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
724 * If the user id supplied is different to the calling user, the caller must hold the
725 * INTERACT_ACROSS_USERS_FULL permission.
728 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
729 enforceCrossUserPermission(userId,
730 "no permission to read the sync settings for user " + userId);
731 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
732 "no permission to read the sync settings");
734 long identityToken = clearCallingIdentity();
736 SyncManager syncManager = getSyncManager();
737 if (syncManager != null) {
738 return syncManager.getSyncStorageEngine()
739 .getSyncAutomatically(account, userId, providerName);
742 restoreCallingIdentity(identityToken);
748 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
749 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
753 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
755 if (TextUtils.isEmpty(providerName)) {
756 throw new IllegalArgumentException("Authority must be non-empty");
758 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
759 "no permission to write the sync settings");
760 enforceCrossUserPermission(userId,
761 "no permission to modify the sync settings for user " + userId);
763 long identityToken = clearCallingIdentity();
765 SyncManager syncManager = getSyncManager();
766 if (syncManager != null) {
767 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
771 restoreCallingIdentity(identityToken);
775 /** Old API. Schedule periodic sync with default flexMillis time. */
777 public void addPeriodicSync(Account account, String authority, Bundle extras,
778 long pollFrequency) {
779 Bundle.setDefusable(extras, true);
780 if (account == null) {
781 throw new IllegalArgumentException("Account must not be null");
783 if (TextUtils.isEmpty(authority)) {
784 throw new IllegalArgumentException("Authority must not be empty.");
786 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
787 "no permission to write the sync settings");
789 int userId = UserHandle.getCallingUserId();
791 pollFrequency = clampPeriod(pollFrequency);
792 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
794 long identityToken = clearCallingIdentity();
796 SyncStorageEngine.EndPoint info =
797 new SyncStorageEngine.EndPoint(account, authority, userId);
798 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
799 defaultFlex, extras);
801 restoreCallingIdentity(identityToken);
806 public void removePeriodicSync(Account account, String authority, Bundle extras) {
807 Bundle.setDefusable(extras, true);
808 if (account == null) {
809 throw new IllegalArgumentException("Account must not be null");
811 if (TextUtils.isEmpty(authority)) {
812 throw new IllegalArgumentException("Authority must not be empty");
814 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
815 "no permission to write the sync settings");
817 int userId = UserHandle.getCallingUserId();
818 long identityToken = clearCallingIdentity();
822 new SyncStorageEngine.EndPoint(account, authority, userId),
825 restoreCallingIdentity(identityToken);
830 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
831 ComponentName cname) {
832 if (account == null) {
833 throw new IllegalArgumentException("Account must not be null");
835 if (TextUtils.isEmpty(providerName)) {
836 throw new IllegalArgumentException("Authority must not be empty");
838 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
839 "no permission to read the sync settings");
841 int userId = UserHandle.getCallingUserId();
842 long identityToken = clearCallingIdentity();
844 return getSyncManager().getPeriodicSyncs(
845 new SyncStorageEngine.EndPoint(account, providerName, userId));
847 restoreCallingIdentity(identityToken);
852 public int getIsSyncable(Account account, String providerName) {
853 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
857 * If the user id supplied is different to the calling user, the caller must hold the
858 * INTERACT_ACROSS_USERS_FULL permission.
861 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
862 enforceCrossUserPermission(userId,
863 "no permission to read the sync settings for user " + userId);
864 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
865 "no permission to read the sync settings");
867 long identityToken = clearCallingIdentity();
869 SyncManager syncManager = getSyncManager();
870 if (syncManager != null) {
871 return syncManager.computeSyncable(
872 account, userId, providerName, false);
875 restoreCallingIdentity(identityToken);
881 public void setIsSyncable(Account account, String providerName, int syncable) {
882 if (TextUtils.isEmpty(providerName)) {
883 throw new IllegalArgumentException("Authority must not be empty");
885 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
886 "no permission to write the sync settings");
888 syncable = normalizeSyncable(syncable);
890 int userId = UserHandle.getCallingUserId();
891 long identityToken = clearCallingIdentity();
893 SyncManager syncManager = getSyncManager();
894 if (syncManager != null) {
895 syncManager.getSyncStorageEngine().setIsSyncable(
896 account, userId, providerName, syncable);
899 restoreCallingIdentity(identityToken);
904 public boolean getMasterSyncAutomatically() {
905 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
909 * If the user id supplied is different to the calling user, the caller must hold the
910 * INTERACT_ACROSS_USERS_FULL permission.
913 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
914 enforceCrossUserPermission(userId,
915 "no permission to read the sync settings for user " + userId);
916 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
917 "no permission to read the sync settings");
919 long identityToken = clearCallingIdentity();
921 SyncManager syncManager = getSyncManager();
922 if (syncManager != null) {
923 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
926 restoreCallingIdentity(identityToken);
932 public void setMasterSyncAutomatically(boolean flag) {
933 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
937 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
938 enforceCrossUserPermission(userId,
939 "no permission to set the sync status for user " + userId);
940 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
941 "no permission to write the sync settings");
943 long identityToken = clearCallingIdentity();
945 SyncManager syncManager = getSyncManager();
946 if (syncManager != null) {
947 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
950 restoreCallingIdentity(identityToken);
955 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
956 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
957 "no permission to read the sync stats");
958 int userId = UserHandle.getCallingUserId();
959 long identityToken = clearCallingIdentity();
961 SyncManager syncManager = getSyncManager();
962 if (syncManager == null) {
965 return syncManager.getSyncStorageEngine().isSyncActive(
966 new SyncStorageEngine.EndPoint(account, authority, userId));
968 restoreCallingIdentity(identityToken);
973 public List<SyncInfo> getCurrentSyncs() {
974 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
978 * If the user id supplied is different to the calling user, the caller must hold the
979 * INTERACT_ACROSS_USERS_FULL permission.
982 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
983 enforceCrossUserPermission(userId,
984 "no permission to read the sync settings for user " + userId);
985 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
986 "no permission to read the sync stats");
988 final boolean canAccessAccounts =
989 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
990 == PackageManager.PERMISSION_GRANTED;
991 long identityToken = clearCallingIdentity();
993 return getSyncManager().getSyncStorageEngine()
994 .getCurrentSyncsCopy(userId, canAccessAccounts);
996 restoreCallingIdentity(identityToken);
1001 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
1002 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
1006 * If the user id supplied is different to the calling user, the caller must hold the
1007 * INTERACT_ACROSS_USERS_FULL permission.
1010 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
1011 ComponentName cname, int userId) {
1012 if (TextUtils.isEmpty(authority)) {
1013 throw new IllegalArgumentException("Authority must not be empty");
1016 enforceCrossUserPermission(userId,
1017 "no permission to read the sync stats for user " + userId);
1018 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1019 "no permission to read the sync stats");
1021 long identityToken = clearCallingIdentity();
1023 SyncManager syncManager = getSyncManager();
1024 if (syncManager == null) {
1027 SyncStorageEngine.EndPoint info;
1028 if (!(account == null || authority == null)) {
1029 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1031 throw new IllegalArgumentException("Must call sync status with valid authority");
1033 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
1035 restoreCallingIdentity(identityToken);
1040 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
1041 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
1045 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
1047 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1048 "no permission to read the sync stats");
1049 enforceCrossUserPermission(userId,
1050 "no permission to retrieve the sync settings for user " + userId);
1051 long identityToken = clearCallingIdentity();
1052 SyncManager syncManager = getSyncManager();
1053 if (syncManager == null) return false;
1056 SyncStorageEngine.EndPoint info;
1057 if (!(account == null || authority == null)) {
1058 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1060 throw new IllegalArgumentException("Invalid authority specified");
1062 return syncManager.getSyncStorageEngine().isSyncPending(info);
1064 restoreCallingIdentity(identityToken);
1069 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1070 long identityToken = clearCallingIdentity();
1072 SyncManager syncManager = getSyncManager();
1073 if (syncManager != null && callback != null) {
1074 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
1077 restoreCallingIdentity(identityToken);
1082 public void removeStatusChangeListener(ISyncStatusObserver callback) {
1083 long identityToken = clearCallingIdentity();
1085 SyncManager syncManager = getSyncManager();
1086 if (syncManager != null && callback != null) {
1087 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
1090 restoreCallingIdentity(identityToken);
1094 private @Nullable String getProviderPackageName(Uri uri) {
1095 final ProviderInfo pi = mContext.getPackageManager()
1096 .resolveContentProvider(uri.getAuthority(), 0);
1097 return (pi != null) ? pi.packageName : null;
1100 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1101 String providerPackageName) {
1102 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1103 if (userCache == null) {
1104 userCache = new ArrayMap<>();
1105 mCache.put(userId, userCache);
1107 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1108 if (packageCache == null) {
1109 packageCache = new ArrayMap<>();
1110 userCache.put(providerPackageName, packageCache);
1112 return packageCache;
1115 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1116 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1117 if (userCache == null) return;
1119 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1120 if (packageCache == null) return;
1123 for (int i = 0; i < packageCache.size();) {
1124 final Pair<String, Uri> key = packageCache.keyAt(i);
1125 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
1126 if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
1127 packageCache.removeAt(i);
1133 if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
1134 packageCache.clear();
1139 public void putCache(String packageName, Uri key, Bundle value, int userId) {
1140 Bundle.setDefusable(value, true);
1141 enforceCrossUserPermission(userId, TAG);
1142 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1143 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1146 final String providerPackageName = getProviderPackageName(key);
1147 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1149 synchronized (mCache) {
1150 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1151 providerPackageName);
1152 if (value != null) {
1153 cache.put(fullKey, value);
1155 cache.remove(fullKey);
1161 public Bundle getCache(String packageName, Uri key, int userId) {
1162 enforceCrossUserPermission(userId, TAG);
1163 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1164 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1167 final String providerPackageName = getProviderPackageName(key);
1168 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1170 synchronized (mCache) {
1171 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1172 providerPackageName);
1173 return cache.get(fullKey);
1177 private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull,
1179 if (userId == UserHandle.USER_CURRENT) {
1180 userId = ActivityManager.getCurrentUser();
1183 if (userId == UserHandle.USER_ALL) {
1184 mContext.enforceCallingOrSelfPermission(
1185 Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
1186 } else if (userId < 0) {
1187 throw new IllegalArgumentException("Invalid user: " + userId);
1188 } else if (userId != UserHandle.getCallingUserId()) {
1189 if (checkUriPermission(uri, pid, uid, modeFlags,
1190 userId) != PackageManager.PERMISSION_GRANTED) {
1191 boolean allow = false;
1192 if (mContext.checkCallingOrSelfPermission(
1193 Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1194 == PackageManager.PERMISSION_GRANTED) {
1196 } else if (allowNonFull && mContext.checkCallingOrSelfPermission(
1197 Manifest.permission.INTERACT_ACROSS_USERS)
1198 == PackageManager.PERMISSION_GRANTED) {
1202 final String permissions = allowNonFull
1203 ? (Manifest.permission.INTERACT_ACROSS_USERS_FULL + " or " +
1204 Manifest.permission.INTERACT_ACROSS_USERS)
1205 : Manifest.permission.INTERACT_ACROSS_USERS_FULL;
1206 throw new SecurityException(TAG + "Neither user " + uid
1207 + " nor current process has " + permissions);
1216 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1217 * permission, if the userHandle is not for the caller.
1219 * @param userHandle the user handle of the user we want to act on behalf of.
1220 * @param message the message to log on security exception.
1222 private void enforceCrossUserPermission(int userHandle, String message) {
1223 final int callingUser = UserHandle.getCallingUserId();
1224 if (callingUser != userHandle) {
1225 mContext.enforceCallingOrSelfPermission(
1226 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1230 private static int normalizeSyncable(int syncable) {
1232 return SyncStorageEngine.AuthorityInfo.SYNCABLE;
1233 } else if (syncable == 0) {
1234 return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1236 return SyncStorageEngine.AuthorityInfo.UNDEFINED;
1240 * Hide this class since it is not part of api,
1241 * but current unittest framework requires it to be public
1244 public static final class ObserverNode {
1245 private class ObserverEntry implements IBinder.DeathRecipient {
1246 public final IContentObserver observer;
1247 public final int uid;
1248 public final int pid;
1249 public final boolean notifyForDescendants;
1250 private final int userHandle;
1251 private final Object observersLock;
1253 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
1254 int _uid, int _pid, int _userHandle) {
1255 this.observersLock = observersLock;
1259 userHandle = _userHandle;
1260 notifyForDescendants = n;
1262 observer.asBinder().linkToDeath(this, 0);
1263 } catch (RemoteException e) {
1269 public void binderDied() {
1270 synchronized (observersLock) {
1271 removeObserverLocked(observer);
1275 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1276 String name, String prefix, SparseIntArray pidCounts) {
1277 pidCounts.put(pid, pidCounts.get(pid)+1);
1278 pw.print(prefix); pw.print(name); pw.print(": pid=");
1279 pw.print(pid); pw.print(" uid=");
1280 pw.print(uid); pw.print(" user=");
1281 pw.print(userHandle); pw.print(" target=");
1282 pw.println(Integer.toHexString(System.identityHashCode(
1283 observer != null ? observer.asBinder() : null)));
1287 public static final int INSERT_TYPE = 0;
1288 public static final int UPDATE_TYPE = 1;
1289 public static final int DELETE_TYPE = 2;
1291 private String mName;
1292 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1293 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1295 public ObserverNode(String name) {
1299 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1300 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
1301 String innerName = null;
1302 if (mObservers.size() > 0) {
1303 if ("".equals(name)) {
1306 innerName = name + "/" + mName;
1308 for (int i=0; i<mObservers.size(); i++) {
1310 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1314 if (mChildren.size() > 0) {
1315 if (innerName == null) {
1316 if ("".equals(name)) {
1319 innerName = name + "/" + mName;
1322 for (int i=0; i<mChildren.size(); i++) {
1324 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1330 private String getUriSegment(Uri uri, int index) {
1333 return uri.getAuthority();
1335 return uri.getPathSegments().get(index - 1);
1342 private int countUriSegments(Uri uri) {
1346 return uri.getPathSegments().size() + 1;
1349 // Invariant: userHandle is either a hard user number or is USER_ALL
1350 public void addObserverLocked(Uri uri, IContentObserver observer,
1351 boolean notifyForDescendants, Object observersLock,
1352 int uid, int pid, int userHandle) {
1353 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1354 uid, pid, userHandle);
1357 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
1358 boolean notifyForDescendants, Object observersLock,
1359 int uid, int pid, int userHandle) {
1360 // If this is the leaf node add the observer
1361 if (index == countUriSegments(uri)) {
1362 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1363 uid, pid, userHandle));
1367 // Look to see if the proper child already exists
1368 String segment = getUriSegment(uri, index);
1369 if (segment == null) {
1370 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1372 int N = mChildren.size();
1373 for (int i = 0; i < N; i++) {
1374 ObserverNode node = mChildren.get(i);
1375 if (node.mName.equals(segment)) {
1376 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1377 observersLock, uid, pid, userHandle);
1382 // No child found, create one
1383 ObserverNode node = new ObserverNode(segment);
1384 mChildren.add(node);
1385 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1386 observersLock, uid, pid, userHandle);
1389 public boolean removeObserverLocked(IContentObserver observer) {
1390 int size = mChildren.size();
1391 for (int i = 0; i < size; i++) {
1392 boolean empty = mChildren.get(i).removeObserverLocked(observer);
1394 mChildren.remove(i);
1400 IBinder observerBinder = observer.asBinder();
1401 size = mObservers.size();
1402 for (int i = 0; i < size; i++) {
1403 ObserverEntry entry = mObservers.get(i);
1404 if (entry.observer.asBinder() == observerBinder) {
1405 mObservers.remove(i);
1406 // We no longer need to listen for death notifications. Remove it.
1407 observerBinder.unlinkToDeath(entry, 0);
1412 if (mChildren.size() == 0 && mObservers.size() == 0) {
1418 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
1419 boolean observerWantsSelfNotifications, int flags,
1420 int targetUserHandle, ArrayList<ObserverCall> calls) {
1421 int N = mObservers.size();
1422 IBinder observerBinder = observer == null ? null : observer.asBinder();
1423 for (int i = 0; i < N; i++) {
1424 ObserverEntry entry = mObservers.get(i);
1426 // Don't notify the observer if it sent the notification and isn't interested
1427 // in self notifications
1428 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1429 if (selfChange && !observerWantsSelfNotifications) {
1433 // Does this observer match the target user?
1434 if (targetUserHandle == UserHandle.USER_ALL
1435 || entry.userHandle == UserHandle.USER_ALL
1436 || targetUserHandle == entry.userHandle) {
1437 // Make sure the observer is interested in the notification
1439 // If we are at the leaf: we always report, unless the sender has asked
1440 // to skip observers that are notifying for descendants (since they will
1441 // be sending another more specific URI for them).
1442 if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1443 && entry.notifyForDescendants) {
1444 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1445 + ": skip notify for descendants");
1449 // If we are not at the leaf: we report if the observer says it wants
1450 // to be notified for all descendants.
1451 if (!entry.notifyForDescendants) {
1452 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1453 + ": not monitor descendants");
1457 if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1458 + " flags=" + Integer.toHexString(flags)
1459 + " desc=" + entry.notifyForDescendants);
1460 calls.add(new ObserverCall(this, entry.observer, selfChange,
1461 UserHandle.getUserId(entry.uid)));
1467 * targetUserHandle is either a hard user handle or is USER_ALL
1469 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
1470 boolean observerWantsSelfNotifications, int flags,
1471 int targetUserHandle, ArrayList<ObserverCall> calls) {
1472 String segment = null;
1473 int segmentCount = countUriSegments(uri);
1474 if (index >= segmentCount) {
1475 // This is the leaf node, notify all observers
1476 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
1477 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1478 flags, targetUserHandle, calls);
1479 } else if (index < segmentCount){
1480 segment = getUriSegment(uri, index);
1481 if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1483 // Notify any observers at this level who are interested in descendants
1484 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1485 flags, targetUserHandle, calls);
1488 int N = mChildren.size();
1489 for (int i = 0; i < N; i++) {
1490 ObserverNode node = mChildren.get(i);
1491 if (segment == null || node.mName.equals(segment)) {
1492 // We found the child,
1493 node.collectObserversLocked(uri, index + 1, observer,
1494 observerWantsSelfNotifications, flags, targetUserHandle, calls);
1495 if (segment != null) {