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();
649 final int callingUid = Binder.getCallingUid();
651 long identityToken = clearCallingIdentity();
653 SyncStorageEngine.EndPoint info;
654 Bundle extras = new Bundle(request.getBundle());
655 Account account = request.getAccount();
656 String provider = request.getProvider();
657 info = new SyncStorageEngine.EndPoint(account, provider, userId);
658 if (request.isPeriodic()) {
659 // Remove periodic sync.
660 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
661 "no permission to write the sync settings");
662 getSyncManager().removePeriodicSync(info, extras,
663 "cancelRequest() by uid=" + callingUid);
665 // Cancel active syncs and clear pending syncs from the queue.
666 syncManager.cancelScheduledSyncOperation(info, extras);
667 syncManager.cancelActiveSync(info, extras, "API");
669 restoreCallingIdentity(identityToken);
674 * Get information about the SyncAdapters that are known to the system.
675 * @return an array of SyncAdapters that have registered with the system
678 public SyncAdapterType[] getSyncAdapterTypes() {
679 return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
683 * Get information about the SyncAdapters that are known to the system for a particular user.
685 * <p> If the user id supplied is different to the calling user, the caller must hold the
686 * INTERACT_ACROSS_USERS_FULL permission.
688 * @return an array of SyncAdapters that have registered with the system
691 public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
692 enforceCrossUserPermission(userId,
693 "no permission to read sync settings for user " + userId);
694 // This makes it so that future permission checks will be in the context of this
695 // process rather than the caller's process. We will restore this before returning.
696 final long identityToken = clearCallingIdentity();
698 SyncManager syncManager = getSyncManager();
699 return syncManager.getSyncAdapterTypes(userId);
701 restoreCallingIdentity(identityToken);
706 public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
707 enforceCrossUserPermission(userId,
708 "no permission to read sync settings for user " + userId);
709 // This makes it so that future permission checks will be in the context of this
710 // process rather than the caller's process. We will restore this before returning.
711 final long identityToken = clearCallingIdentity();
713 SyncManager syncManager = getSyncManager();
714 return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
716 restoreCallingIdentity(identityToken);
721 public boolean getSyncAutomatically(Account account, String providerName) {
722 return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
726 * If the user id supplied is different to the calling user, the caller must hold the
727 * INTERACT_ACROSS_USERS_FULL permission.
730 public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
731 enforceCrossUserPermission(userId,
732 "no permission to read the sync settings for user " + userId);
733 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
734 "no permission to read the sync settings");
736 long identityToken = clearCallingIdentity();
738 SyncManager syncManager = getSyncManager();
739 if (syncManager != null) {
740 return syncManager.getSyncStorageEngine()
741 .getSyncAutomatically(account, userId, providerName);
744 restoreCallingIdentity(identityToken);
750 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
751 setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
755 public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
757 if (TextUtils.isEmpty(providerName)) {
758 throw new IllegalArgumentException("Authority must be non-empty");
760 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
761 "no permission to write the sync settings");
762 enforceCrossUserPermission(userId,
763 "no permission to modify the sync settings for user " + userId);
765 long identityToken = clearCallingIdentity();
767 SyncManager syncManager = getSyncManager();
768 if (syncManager != null) {
769 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
773 restoreCallingIdentity(identityToken);
777 /** Old API. Schedule periodic sync with default flexMillis time. */
779 public void addPeriodicSync(Account account, String authority, Bundle extras,
780 long pollFrequency) {
781 Bundle.setDefusable(extras, true);
782 if (account == null) {
783 throw new IllegalArgumentException("Account must not be null");
785 if (TextUtils.isEmpty(authority)) {
786 throw new IllegalArgumentException("Authority must not be empty.");
788 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
789 "no permission to write the sync settings");
791 int userId = UserHandle.getCallingUserId();
793 pollFrequency = clampPeriod(pollFrequency);
794 long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
796 long identityToken = clearCallingIdentity();
798 SyncStorageEngine.EndPoint info =
799 new SyncStorageEngine.EndPoint(account, authority, userId);
800 getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
801 defaultFlex, extras);
803 restoreCallingIdentity(identityToken);
808 public void removePeriodicSync(Account account, String authority, Bundle extras) {
809 Bundle.setDefusable(extras, true);
810 if (account == null) {
811 throw new IllegalArgumentException("Account must not be null");
813 if (TextUtils.isEmpty(authority)) {
814 throw new IllegalArgumentException("Authority must not be empty");
816 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
817 "no permission to write the sync settings");
819 final int callingUid = Binder.getCallingUid();
821 int userId = UserHandle.getCallingUserId();
822 long identityToken = clearCallingIdentity();
826 new SyncStorageEngine.EndPoint(account, authority, userId),
827 extras, "removePeriodicSync() by uid=" + callingUid);
829 restoreCallingIdentity(identityToken);
834 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
835 ComponentName cname) {
836 if (account == null) {
837 throw new IllegalArgumentException("Account must not be null");
839 if (TextUtils.isEmpty(providerName)) {
840 throw new IllegalArgumentException("Authority must not be empty");
842 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
843 "no permission to read the sync settings");
845 int userId = UserHandle.getCallingUserId();
846 long identityToken = clearCallingIdentity();
848 return getSyncManager().getPeriodicSyncs(
849 new SyncStorageEngine.EndPoint(account, providerName, userId));
851 restoreCallingIdentity(identityToken);
856 public int getIsSyncable(Account account, String providerName) {
857 return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
861 * If the user id supplied is different to the calling user, the caller must hold the
862 * INTERACT_ACROSS_USERS_FULL permission.
865 public int getIsSyncableAsUser(Account account, String providerName, int userId) {
866 enforceCrossUserPermission(userId,
867 "no permission to read the sync settings for user " + userId);
868 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
869 "no permission to read the sync settings");
871 long identityToken = clearCallingIdentity();
873 SyncManager syncManager = getSyncManager();
874 if (syncManager != null) {
875 return syncManager.computeSyncable(
876 account, userId, providerName, false);
879 restoreCallingIdentity(identityToken);
885 public void setIsSyncable(Account account, String providerName, int syncable) {
886 if (TextUtils.isEmpty(providerName)) {
887 throw new IllegalArgumentException("Authority must not be empty");
889 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
890 "no permission to write the sync settings");
892 syncable = normalizeSyncable(syncable);
894 int userId = UserHandle.getCallingUserId();
895 long identityToken = clearCallingIdentity();
897 SyncManager syncManager = getSyncManager();
898 if (syncManager != null) {
899 syncManager.getSyncStorageEngine().setIsSyncable(
900 account, userId, providerName, syncable);
903 restoreCallingIdentity(identityToken);
908 public boolean getMasterSyncAutomatically() {
909 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
913 * If the user id supplied is different to the calling user, the caller must hold the
914 * INTERACT_ACROSS_USERS_FULL permission.
917 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
918 enforceCrossUserPermission(userId,
919 "no permission to read the sync settings for user " + userId);
920 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
921 "no permission to read the sync settings");
923 long identityToken = clearCallingIdentity();
925 SyncManager syncManager = getSyncManager();
926 if (syncManager != null) {
927 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
930 restoreCallingIdentity(identityToken);
936 public void setMasterSyncAutomatically(boolean flag) {
937 setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
941 public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
942 enforceCrossUserPermission(userId,
943 "no permission to set the sync status for user " + userId);
944 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
945 "no permission to write the sync settings");
947 long identityToken = clearCallingIdentity();
949 SyncManager syncManager = getSyncManager();
950 if (syncManager != null) {
951 syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
954 restoreCallingIdentity(identityToken);
959 public boolean isSyncActive(Account account, String authority, ComponentName cname) {
960 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
961 "no permission to read the sync stats");
962 int userId = UserHandle.getCallingUserId();
963 long identityToken = clearCallingIdentity();
965 SyncManager syncManager = getSyncManager();
966 if (syncManager == null) {
969 return syncManager.getSyncStorageEngine().isSyncActive(
970 new SyncStorageEngine.EndPoint(account, authority, userId));
972 restoreCallingIdentity(identityToken);
977 public List<SyncInfo> getCurrentSyncs() {
978 return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
982 * If the user id supplied is different to the calling user, the caller must hold the
983 * INTERACT_ACROSS_USERS_FULL permission.
986 public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
987 enforceCrossUserPermission(userId,
988 "no permission to read the sync settings for user " + userId);
989 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
990 "no permission to read the sync stats");
992 final boolean canAccessAccounts =
993 mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
994 == PackageManager.PERMISSION_GRANTED;
995 long identityToken = clearCallingIdentity();
997 return getSyncManager().getSyncStorageEngine()
998 .getCurrentSyncsCopy(userId, canAccessAccounts);
1000 restoreCallingIdentity(identityToken);
1005 public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
1006 return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
1010 * If the user id supplied is different to the calling user, the caller must hold the
1011 * INTERACT_ACROSS_USERS_FULL permission.
1014 public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
1015 ComponentName cname, int userId) {
1016 if (TextUtils.isEmpty(authority)) {
1017 throw new IllegalArgumentException("Authority must not be empty");
1020 enforceCrossUserPermission(userId,
1021 "no permission to read the sync stats for user " + userId);
1022 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1023 "no permission to read the sync stats");
1025 long identityToken = clearCallingIdentity();
1027 SyncManager syncManager = getSyncManager();
1028 if (syncManager == null) {
1031 SyncStorageEngine.EndPoint info;
1032 if (!(account == null || authority == null)) {
1033 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1035 throw new IllegalArgumentException("Must call sync status with valid authority");
1037 return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
1039 restoreCallingIdentity(identityToken);
1044 public boolean isSyncPending(Account account, String authority, ComponentName cname) {
1045 return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
1049 public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
1051 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
1052 "no permission to read the sync stats");
1053 enforceCrossUserPermission(userId,
1054 "no permission to retrieve the sync settings for user " + userId);
1055 long identityToken = clearCallingIdentity();
1056 SyncManager syncManager = getSyncManager();
1057 if (syncManager == null) return false;
1060 SyncStorageEngine.EndPoint info;
1061 if (!(account == null || authority == null)) {
1062 info = new SyncStorageEngine.EndPoint(account, authority, userId);
1064 throw new IllegalArgumentException("Invalid authority specified");
1066 return syncManager.getSyncStorageEngine().isSyncPending(info);
1068 restoreCallingIdentity(identityToken);
1073 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
1074 long identityToken = clearCallingIdentity();
1076 SyncManager syncManager = getSyncManager();
1077 if (syncManager != null && callback != null) {
1078 syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
1081 restoreCallingIdentity(identityToken);
1086 public void removeStatusChangeListener(ISyncStatusObserver callback) {
1087 long identityToken = clearCallingIdentity();
1089 SyncManager syncManager = getSyncManager();
1090 if (syncManager != null && callback != null) {
1091 syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
1094 restoreCallingIdentity(identityToken);
1098 private @Nullable String getProviderPackageName(Uri uri) {
1099 final ProviderInfo pi = mContext.getPackageManager()
1100 .resolveContentProvider(uri.getAuthority(), 0);
1101 return (pi != null) ? pi.packageName : null;
1104 private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
1105 String providerPackageName) {
1106 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1107 if (userCache == null) {
1108 userCache = new ArrayMap<>();
1109 mCache.put(userId, userCache);
1111 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1112 if (packageCache == null) {
1113 packageCache = new ArrayMap<>();
1114 userCache.put(providerPackageName, packageCache);
1116 return packageCache;
1119 private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
1120 ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
1121 if (userCache == null) return;
1123 ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
1124 if (packageCache == null) return;
1127 for (int i = 0; i < packageCache.size();) {
1128 final Pair<String, Uri> key = packageCache.keyAt(i);
1129 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
1130 if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
1131 packageCache.removeAt(i);
1137 if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
1138 packageCache.clear();
1143 public void putCache(String packageName, Uri key, Bundle value, int userId) {
1144 Bundle.setDefusable(value, true);
1145 enforceCrossUserPermission(userId, TAG);
1146 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1147 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1150 final String providerPackageName = getProviderPackageName(key);
1151 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1153 synchronized (mCache) {
1154 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1155 providerPackageName);
1156 if (value != null) {
1157 cache.put(fullKey, value);
1159 cache.remove(fullKey);
1165 public Bundle getCache(String packageName, Uri key, int userId) {
1166 enforceCrossUserPermission(userId, TAG);
1167 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
1168 mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
1171 final String providerPackageName = getProviderPackageName(key);
1172 final Pair<String, Uri> fullKey = Pair.create(packageName, key);
1174 synchronized (mCache) {
1175 final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
1176 providerPackageName);
1177 return cache.get(fullKey);
1181 private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull,
1183 if (userId == UserHandle.USER_CURRENT) {
1184 userId = ActivityManager.getCurrentUser();
1187 if (userId == UserHandle.USER_ALL) {
1188 mContext.enforceCallingOrSelfPermission(
1189 Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
1190 } else if (userId < 0) {
1191 throw new IllegalArgumentException("Invalid user: " + userId);
1192 } else if (userId != UserHandle.getCallingUserId()) {
1193 if (checkUriPermission(uri, pid, uid, modeFlags,
1194 userId) != PackageManager.PERMISSION_GRANTED) {
1195 boolean allow = false;
1196 if (mContext.checkCallingOrSelfPermission(
1197 Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1198 == PackageManager.PERMISSION_GRANTED) {
1200 } else if (allowNonFull && mContext.checkCallingOrSelfPermission(
1201 Manifest.permission.INTERACT_ACROSS_USERS)
1202 == PackageManager.PERMISSION_GRANTED) {
1206 final String permissions = allowNonFull
1207 ? (Manifest.permission.INTERACT_ACROSS_USERS_FULL + " or " +
1208 Manifest.permission.INTERACT_ACROSS_USERS)
1209 : Manifest.permission.INTERACT_ACROSS_USERS_FULL;
1210 throw new SecurityException(TAG + "Neither user " + uid
1211 + " nor current process has " + permissions);
1220 * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
1221 * permission, if the userHandle is not for the caller.
1223 * @param userHandle the user handle of the user we want to act on behalf of.
1224 * @param message the message to log on security exception.
1226 private void enforceCrossUserPermission(int userHandle, String message) {
1227 final int callingUser = UserHandle.getCallingUserId();
1228 if (callingUser != userHandle) {
1229 mContext.enforceCallingOrSelfPermission(
1230 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
1234 private static int normalizeSyncable(int syncable) {
1236 return SyncStorageEngine.AuthorityInfo.SYNCABLE;
1237 } else if (syncable == 0) {
1238 return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE;
1240 return SyncStorageEngine.AuthorityInfo.UNDEFINED;
1244 * Hide this class since it is not part of api,
1245 * but current unittest framework requires it to be public
1248 public static final class ObserverNode {
1249 private class ObserverEntry implements IBinder.DeathRecipient {
1250 public final IContentObserver observer;
1251 public final int uid;
1252 public final int pid;
1253 public final boolean notifyForDescendants;
1254 private final int userHandle;
1255 private final Object observersLock;
1257 public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
1258 int _uid, int _pid, int _userHandle) {
1259 this.observersLock = observersLock;
1263 userHandle = _userHandle;
1264 notifyForDescendants = n;
1266 observer.asBinder().linkToDeath(this, 0);
1267 } catch (RemoteException e) {
1273 public void binderDied() {
1274 synchronized (observersLock) {
1275 removeObserverLocked(observer);
1279 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1280 String name, String prefix, SparseIntArray pidCounts) {
1281 pidCounts.put(pid, pidCounts.get(pid)+1);
1282 pw.print(prefix); pw.print(name); pw.print(": pid=");
1283 pw.print(pid); pw.print(" uid=");
1284 pw.print(uid); pw.print(" user=");
1285 pw.print(userHandle); pw.print(" target=");
1286 pw.println(Integer.toHexString(System.identityHashCode(
1287 observer != null ? observer.asBinder() : null)));
1291 public static final int INSERT_TYPE = 0;
1292 public static final int UPDATE_TYPE = 1;
1293 public static final int DELETE_TYPE = 2;
1295 private String mName;
1296 private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
1297 private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
1299 public ObserverNode(String name) {
1303 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
1304 String name, String prefix, int[] counts, SparseIntArray pidCounts) {
1305 String innerName = null;
1306 if (mObservers.size() > 0) {
1307 if ("".equals(name)) {
1310 innerName = name + "/" + mName;
1312 for (int i=0; i<mObservers.size(); i++) {
1314 mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1318 if (mChildren.size() > 0) {
1319 if (innerName == null) {
1320 if ("".equals(name)) {
1323 innerName = name + "/" + mName;
1326 for (int i=0; i<mChildren.size(); i++) {
1328 mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
1334 private String getUriSegment(Uri uri, int index) {
1337 return uri.getAuthority();
1339 return uri.getPathSegments().get(index - 1);
1346 private int countUriSegments(Uri uri) {
1350 return uri.getPathSegments().size() + 1;
1353 // Invariant: userHandle is either a hard user number or is USER_ALL
1354 public void addObserverLocked(Uri uri, IContentObserver observer,
1355 boolean notifyForDescendants, Object observersLock,
1356 int uid, int pid, int userHandle) {
1357 addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
1358 uid, pid, userHandle);
1361 private void addObserverLocked(Uri uri, int index, IContentObserver observer,
1362 boolean notifyForDescendants, Object observersLock,
1363 int uid, int pid, int userHandle) {
1364 // If this is the leaf node add the observer
1365 if (index == countUriSegments(uri)) {
1366 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
1367 uid, pid, userHandle));
1371 // Look to see if the proper child already exists
1372 String segment = getUriSegment(uri, index);
1373 if (segment == null) {
1374 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
1376 int N = mChildren.size();
1377 for (int i = 0; i < N; i++) {
1378 ObserverNode node = mChildren.get(i);
1379 if (node.mName.equals(segment)) {
1380 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1381 observersLock, uid, pid, userHandle);
1386 // No child found, create one
1387 ObserverNode node = new ObserverNode(segment);
1388 mChildren.add(node);
1389 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
1390 observersLock, uid, pid, userHandle);
1393 public boolean removeObserverLocked(IContentObserver observer) {
1394 int size = mChildren.size();
1395 for (int i = 0; i < size; i++) {
1396 boolean empty = mChildren.get(i).removeObserverLocked(observer);
1398 mChildren.remove(i);
1404 IBinder observerBinder = observer.asBinder();
1405 size = mObservers.size();
1406 for (int i = 0; i < size; i++) {
1407 ObserverEntry entry = mObservers.get(i);
1408 if (entry.observer.asBinder() == observerBinder) {
1409 mObservers.remove(i);
1410 // We no longer need to listen for death notifications. Remove it.
1411 observerBinder.unlinkToDeath(entry, 0);
1416 if (mChildren.size() == 0 && mObservers.size() == 0) {
1422 private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
1423 boolean observerWantsSelfNotifications, int flags,
1424 int targetUserHandle, ArrayList<ObserverCall> calls) {
1425 int N = mObservers.size();
1426 IBinder observerBinder = observer == null ? null : observer.asBinder();
1427 for (int i = 0; i < N; i++) {
1428 ObserverEntry entry = mObservers.get(i);
1430 // Don't notify the observer if it sent the notification and isn't interested
1431 // in self notifications
1432 boolean selfChange = (entry.observer.asBinder() == observerBinder);
1433 if (selfChange && !observerWantsSelfNotifications) {
1437 // Does this observer match the target user?
1438 if (targetUserHandle == UserHandle.USER_ALL
1439 || entry.userHandle == UserHandle.USER_ALL
1440 || targetUserHandle == entry.userHandle) {
1441 // Make sure the observer is interested in the notification
1443 // If we are at the leaf: we always report, unless the sender has asked
1444 // to skip observers that are notifying for descendants (since they will
1445 // be sending another more specific URI for them).
1446 if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
1447 && entry.notifyForDescendants) {
1448 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1449 + ": skip notify for descendants");
1453 // If we are not at the leaf: we report if the observer says it wants
1454 // to be notified for all descendants.
1455 if (!entry.notifyForDescendants) {
1456 if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
1457 + ": not monitor descendants");
1461 if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
1462 + " flags=" + Integer.toHexString(flags)
1463 + " desc=" + entry.notifyForDescendants);
1464 calls.add(new ObserverCall(this, entry.observer, selfChange,
1465 UserHandle.getUserId(entry.uid)));
1471 * targetUserHandle is either a hard user handle or is USER_ALL
1473 public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
1474 boolean observerWantsSelfNotifications, int flags,
1475 int targetUserHandle, ArrayList<ObserverCall> calls) {
1476 String segment = null;
1477 int segmentCount = countUriSegments(uri);
1478 if (index >= segmentCount) {
1479 // This is the leaf node, notify all observers
1480 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
1481 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
1482 flags, targetUserHandle, calls);
1483 } else if (index < segmentCount){
1484 segment = getUriSegment(uri, index);
1485 if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
1487 // Notify any observers at this level who are interested in descendants
1488 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
1489 flags, targetUserHandle, calls);
1492 int N = mChildren.size();
1493 for (int i = 0; i < N; i++) {
1494 ObserverNode node = mChildren.get(i);
1495 if (segment == null || node.mName.equals(segment)) {
1496 // We found the child,
1497 node.collectObserversLocked(uri, index + 1, observer,
1498 observerWantsSelfNotifications, flags, targetUserHandle, calls);
1499 if (segment != null) {