/services/core/java/com/android/server/content/ContentService.java
Java | 1213 lines | 949 code | 113 blank | 151 comment | 153 complexity | 85ad4e6ba22d7d59fe53369252828e09 MD5 | raw file
- /*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.server.content;
- import android.Manifest;
- import android.accounts.Account;
- import android.annotation.Nullable;
- import android.app.ActivityManager;
- import android.app.ActivityManagerNative;
- import android.app.AppOpsManager;
- import android.app.job.JobInfo;
- import android.content.BroadcastReceiver;
- import android.content.ComponentName;
- import android.content.ContentProvider;
- import android.content.ContentResolver;
- import android.content.Context;
- import android.content.IContentService;
- import android.content.ISyncStatusObserver;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.PeriodicSync;
- import android.content.SyncAdapterType;
- import android.content.SyncInfo;
- import android.content.SyncRequest;
- import android.content.SyncStatusInfo;
- import android.content.pm.PackageManager;
- import android.content.pm.PackageManagerInternal;
- import android.content.pm.ProviderInfo;
- import android.database.IContentObserver;
- import android.database.sqlite.SQLiteException;
- import android.net.Uri;
- import android.os.Binder;
- import android.os.Bundle;
- import android.os.FactoryTest;
- import android.os.IBinder;
- import android.os.Parcel;
- import android.os.RemoteException;
- import android.os.SystemProperties;
- import android.os.UserHandle;
- import android.text.TextUtils;
- import android.util.ArrayMap;
- import android.util.Log;
- import android.util.Pair;
- import android.util.Slog;
- import android.util.SparseArray;
- import android.util.SparseIntArray;
- import com.android.internal.annotations.GuardedBy;
- import com.android.internal.util.IndentingPrintWriter;
- import com.android.server.LocalServices;
- import com.android.server.SystemService;
- import java.io.FileDescriptor;
- import java.io.PrintWriter;
- import java.security.InvalidParameterException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.List;
- /**
- * {@hide}
- */
- public final class ContentService extends IContentService.Stub {
- static final String TAG = "ContentService";
- static final boolean DEBUG = false;
- public static class Lifecycle extends SystemService {
- private ContentService mService;
- public Lifecycle(Context context) {
- super(context);
- }
- @Override
- public void onStart() {
- final boolean factoryTest = (FactoryTest
- .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
- mService = new ContentService(getContext(), factoryTest);
- publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
- }
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mService.systemReady();
- }
- }
- @Override
- public void onCleanupUser(int userHandle) {
- synchronized (mService.mCache) {
- mService.mCache.remove(userHandle);
- }
- }
- }
- private Context mContext;
- private boolean mFactoryTest;
- private final ObserverNode mRootNode = new ObserverNode("");
- private SyncManager mSyncManager = null;
- private final Object mSyncManagerLock = new Object();
- /**
- * Map from userId to providerPackageName to [clientPackageName, uri] to
- * value. This structure is carefully optimized to keep invalidation logic
- * as cheap as possible.
- */
- @GuardedBy("mCache")
- private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
- mCache = new SparseArray<>();
- private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mCache) {
- if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
- mCache.clear();
- } else {
- final Uri data = intent.getData();
- if (data != null) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- final String packageName = data.getSchemeSpecificPart();
- invalidateCacheLocked(userId, packageName, null);
- }
- }
- }
- }
- };
- private SyncManager getSyncManager() {
- if (SystemProperties.getBoolean("config.disable_network", false)) {
- return null;
- }
- synchronized(mSyncManagerLock) {
- try {
- // Try to create the SyncManager, return null if it fails (e.g. the disk is full).
- if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest);
- } catch (SQLiteException e) {
- Log.e(TAG, "Can't create SyncManager", e);
- }
- return mSyncManager;
- }
- }
- @Override
- protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
- "caller doesn't have the DUMP permission");
- final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- final long identityToken = clearCallingIdentity();
- try {
- if (mSyncManager == null) {
- pw.println("No SyncManager created! (Disk full?)");
- } else {
- mSyncManager.dump(fd, pw);
- }
- pw.println();
- pw.println("Observer tree:");
- synchronized (mRootNode) {
- int[] counts = new int[2];
- final SparseIntArray pidCounts = new SparseIntArray();
- mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts);
- pw.println();
- ArrayList<Integer> sorted = new ArrayList<Integer>();
- for (int i=0; i<pidCounts.size(); i++) {
- sorted.add(pidCounts.keyAt(i));
- }
- Collections.sort(sorted, new Comparator<Integer>() {
- @Override
- public int compare(Integer lhs, Integer rhs) {
- int lc = pidCounts.get(lhs);
- int rc = pidCounts.get(rhs);
- if (lc < rc) {
- return 1;
- } else if (lc > rc) {
- return -1;
- }
- return 0;
- }
- });
- for (int i=0; i<sorted.size(); i++) {
- int pid = sorted.get(i);
- pw.print(" pid "); pw.print(pid); pw.print(": ");
- pw.print(pidCounts.get(pid)); pw.println(" observers");
- }
- pw.println();
- pw.print(" Total number of nodes: "); pw.println(counts[0]);
- pw.print(" Total number of observers: "); pw.println(counts[1]);
- }
- synchronized (mCache) {
- pw.println();
- pw.println("Cached content:");
- pw.increaseIndent();
- for (int i = 0; i < mCache.size(); i++) {
- pw.println("User " + mCache.keyAt(i) + ":");
- pw.increaseIndent();
- pw.println(mCache.valueAt(i));
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (RuntimeException e) {
- // The content service only throws security exceptions, so let's
- // log all others.
- if (!(e instanceof SecurityException)) {
- Slog.wtf(TAG, "Content Service Crash", e);
- }
- throw e;
- }
- }
- /*package*/ ContentService(Context context, boolean factoryTest) {
- mContext = context;
- mFactoryTest = factoryTest;
- // Let the package manager query for the sync adapters for a given authority
- // as we grant default permissions to sync adapters for specific authorities.
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- packageManagerInternal.setSyncAdapterPackagesprovider(
- new PackageManagerInternal.SyncAdapterPackagesProvider() {
- @Override
- public String[] getPackages(String authority, int userId) {
- return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
- }
- });
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
- packageFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
- packageFilter, null, null);
- final IntentFilter localeFilter = new IntentFilter();
- localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
- mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
- localeFilter, null, null);
- }
- void systemReady() {
- getSyncManager();
- }
- /**
- * Register a content observer tied to a specific user's view of the provider.
- * @param userHandle the user whose view of the provider is to be observed. May be
- * the calling user without requiring any permission, otherwise the caller needs to
- * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
- * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
- * are forbidden.
- */
- @Override
- public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle) {
- if (observer == null || uri == null) {
- throw new IllegalArgumentException("You must pass a valid uri and observer");
- }
- final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
- final int callingUserHandle = UserHandle.getCallingUserId();
- // Registering an observer for any user other than the calling user requires uri grant or
- // cross user permission
- if (callingUserHandle != userHandle) {
- if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle)
- != PackageManager.PERMISSION_GRANTED) {
- enforceCrossUserPermission(userHandle,
- "no permission to observe other users' provider view");
- }
- }
- if (userHandle < 0) {
- if (userHandle == UserHandle.USER_CURRENT) {
- userHandle = ActivityManager.getCurrentUser();
- } else if (userHandle != UserHandle.USER_ALL) {
- throw new InvalidParameterException("Bad user handle for registerContentObserver: "
- + userHandle);
- }
- }
- synchronized (mRootNode) {
- mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
- uid, pid, userHandle);
- if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
- " with notifyForDescendants " + notifyForDescendants);
- }
- }
- public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer) {
- registerContentObserver(uri, notifyForDescendants, observer,
- UserHandle.getCallingUserId());
- }
- @Override
- public void unregisterContentObserver(IContentObserver observer) {
- if (observer == null) {
- throw new IllegalArgumentException("You must pass a valid observer");
- }
- synchronized (mRootNode) {
- mRootNode.removeObserverLocked(observer);
- if (false) Log.v(TAG, "Unregistered observer " + observer);
- }
- }
- /**
- * Notify observers of a particular user's view of the provider.
- * @param userHandle the user whose view of the provider is to be notified. May be
- * the calling user without requiring any permission, otherwise the caller needs to
- * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
- * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
- * allowed.
- */
- @Override
- public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, int flags,
- int userHandle) {
- if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
- + " from observer " + observer + ", flags " + Integer.toHexString(flags));
- if (uri == null) {
- throw new NullPointerException("Uri must not be null");
- }
- final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
- final int callingUserHandle = UserHandle.getCallingUserId();
- // Notify for any user other than the caller requires uri grant or cross user permission
- if (callingUserHandle != userHandle) {
- if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- userHandle) != PackageManager.PERMISSION_GRANTED) {
- enforceCrossUserPermission(userHandle, "no permission to notify other users");
- }
- }
- // We passed the permission check; resolve pseudouser targets as appropriate
- if (userHandle < 0) {
- if (userHandle == UserHandle.USER_CURRENT) {
- userHandle = ActivityManager.getCurrentUser();
- } else if (userHandle != UserHandle.USER_ALL) {
- throw new InvalidParameterException("Bad user handle for notifyChange: "
- + userHandle);
- }
- }
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
- try {
- ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
- synchronized (mRootNode) {
- mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
- flags, userHandle, calls);
- }
- final int numCalls = calls.size();
- for (int i=0; i<numCalls; i++) {
- ObserverCall oc = calls.get(i);
- try {
- oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
- if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
- + uri);
- } catch (RemoteException ex) {
- synchronized (mRootNode) {
- Log.w(TAG, "Found dead observer, removing");
- IBinder binder = oc.mObserver.asBinder();
- final ArrayList<ObserverNode.ObserverEntry> list
- = oc.mNode.mObservers;
- int numList = list.size();
- for (int j=0; j<numList; j++) {
- ObserverNode.ObserverEntry oe = list.get(j);
- if (oe.observer.asBinder() == binder) {
- list.remove(j);
- j--;
- numList--;
- }
- }
- }
- }
- }
- if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
- uri != null ? uri.getAuthority() : null);
- }
- }
- synchronized (mCache) {
- final String providerPackageName = getProviderPackageName(uri);
- invalidateCacheLocked(userHandle, providerPackageName, uri);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- private int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, int userHandle) {
- try {
- return ActivityManagerNative.getDefault().checkUriPermission(
- uri, pid, uid, modeFlags, userHandle, null);
- } catch (RemoteException e) {
- return PackageManager.PERMISSION_DENIED;
- }
- }
- public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, boolean syncToNetwork) {
- notifyChange(uri, observer, observerWantsSelfNotifications,
- syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
- UserHandle.getCallingUserId());
- }
- /**
- * Hide this class since it is not part of api,
- * but current unittest framework requires it to be public
- * @hide
- *
- */
- public static final class ObserverCall {
- final ObserverNode mNode;
- final IContentObserver mObserver;
- final boolean mSelfChange;
- final int mObserverUserId;
- ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange, int observerUserId) {
- mNode = node;
- mObserver = observer;
- mSelfChange = selfChange;
- mObserverUserId = observerUserId;
- }
- }
- @Override
- public void requestSync(Account account, String authority, Bundle extras) {
- Bundle.setDefusable(extras, true);
- ContentResolver.validateSyncExtrasBundle(extras);
- int userId = UserHandle.getCallingUserId();
- int uId = Binder.getCallingUid();
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.scheduleSync(account, userId, uId, authority, extras,
- 0 /* no delay */, 0 /* no delay */,
- false /* onlyThoseWithUnkownSyncableState */);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- /**
- * Request a sync with a generic {@link android.content.SyncRequest} object. This will be
- * either:
- * periodic OR one-off sync.
- * and
- * anonymous OR provider sync.
- * Depending on the request, we enqueue to suit in the SyncManager.
- * @param request The request object. Validation of this object is done by its builder.
- */
- @Override
- public void sync(SyncRequest request) {
- syncAsUser(request, UserHandle.getCallingUserId());
- }
- private long clampPeriod(long period) {
- long minPeriod = JobInfo.getMinPeriodMillis() / 1000;
- if (period < minPeriod) {
- Slog.w(TAG, "Requested poll frequency of " + period
- + " seconds being rounded up to " + minPeriod + "s.");
- period = minPeriod;
- }
- return period;
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public void syncAsUser(SyncRequest request, int userId) {
- enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
- int callerUid = Binder.getCallingUid();
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager == null) {
- return;
- }
- Bundle extras = request.getBundle();
- long flextime = request.getSyncFlexTime();
- long runAtTime = request.getSyncRunTime();
- if (request.isPeriodic()) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- SyncStorageEngine.EndPoint info;
- info = new SyncStorageEngine.EndPoint(
- request.getAccount(), request.getProvider(), userId);
- runAtTime = clampPeriod(runAtTime);
- // Schedule periodic sync.
- getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
- flextime, extras);
- } else {
- long beforeRuntimeMillis = (flextime) * 1000;
- long runtimeMillis = runAtTime * 1000;
- syncManager.scheduleSync(
- request.getAccount(), userId, callerUid, request.getProvider(), extras,
- beforeRuntimeMillis, runtimeMillis,
- false /* onlyThoseWithUnknownSyncableState */);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- /**
- * Clear all scheduled sync operations that match the uri and cancel the active sync
- * if they match the authority and account, if they are present.
- *
- * @param account filter the pending and active syncs to cancel using this account, or null.
- * @param authority filter the pending and active syncs to cancel using this authority, or
- * null.
- * @param cname cancel syncs running on this service, or null for provider/account.
- */
- @Override
- public void cancelSync(Account account, String authority, ComponentName cname) {
- cancelSyncAsUser(account, authority, cname, UserHandle.getCallingUserId());
- }
- /**
- * Clear all scheduled sync operations that match the uri and cancel the active sync
- * if they match the authority and account, if they are present.
- *
- * <p> If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- *
- * @param account filter the pending and active syncs to cancel using this account, or null.
- * @param authority filter the pending and active syncs to cancel using this authority, or
- * null.
- * @param userId the user id for which to cancel sync operations.
- * @param cname cancel syncs running on this service, or null for provider/account.
- */
- @Override
- public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
- int userId) {
- if (authority != null && authority.length() == 0) {
- throw new IllegalArgumentException("Authority must be non-empty");
- }
- enforceCrossUserPermission(userId,
- "no permission to modify the sync settings for user " + userId);
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
- if (cname != null) {
- Slog.e(TAG, "cname not null.");
- return;
- }
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- SyncStorageEngine.EndPoint info;
- info = new SyncStorageEngine.EndPoint(account, authority, userId);
- syncManager.clearScheduledSyncOperations(info);
- syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public void cancelRequest(SyncRequest request) {
- SyncManager syncManager = getSyncManager();
- if (syncManager == null) return;
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- SyncStorageEngine.EndPoint info;
- Bundle extras = new Bundle(request.getBundle());
- Account account = request.getAccount();
- String provider = request.getProvider();
- info = new SyncStorageEngine.EndPoint(account, provider, userId);
- if (request.isPeriodic()) {
- // Remove periodic sync.
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- getSyncManager().removePeriodicSync(info, extras);
- }
- // Cancel active syncs and clear pending syncs from the queue.
- syncManager.cancelScheduledSyncOperation(info, extras);
- syncManager.cancelActiveSync(info, extras);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- /**
- * Get information about the SyncAdapters that are known to the system.
- * @return an array of SyncAdapters that have registered with the system
- */
- @Override
- public SyncAdapterType[] getSyncAdapterTypes() {
- return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId());
- }
- /**
- * Get information about the SyncAdapters that are known to the system for a particular user.
- *
- * <p> If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- *
- * @return an array of SyncAdapters that have registered with the system
- */
- @Override
- public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read sync settings for user " + userId);
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- final long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- return syncManager.getSyncAdapterTypes(userId);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read sync settings for user " + userId);
- // This makes it so that future permission checks will be in the context of this
- // process rather than the caller's process. We will restore this before returning.
- final long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public boolean getSyncAutomatically(Account account, String providerName) {
- return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public boolean getSyncAutomaticallyAsUser(Account account, String providerName, int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read the sync settings for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
- "no permission to read the sync settings");
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getSyncStorageEngine()
- .getSyncAutomatically(account, userId, providerName);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- return false;
- }
- @Override
- public void setSyncAutomatically(Account account, String providerName, boolean sync) {
- setSyncAutomaticallyAsUser(account, providerName, sync, UserHandle.getCallingUserId());
- }
- @Override
- public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
- int userId) {
- if (TextUtils.isEmpty(providerName)) {
- throw new IllegalArgumentException("Authority must be non-empty");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- enforceCrossUserPermission(userId,
- "no permission to modify the sync settings for user " + userId);
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
- providerName, sync);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- /** Old API. Schedule periodic sync with default flexMillis time. */
- @Override
- public void addPeriodicSync(Account account, String authority, Bundle extras,
- long pollFrequency) {
- Bundle.setDefusable(extras, true);
- if (account == null) {
- throw new IllegalArgumentException("Account must not be null");
- }
- if (TextUtils.isEmpty(authority)) {
- throw new IllegalArgumentException("Authority must not be empty.");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- int userId = UserHandle.getCallingUserId();
- pollFrequency = clampPeriod(pollFrequency);
- long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
- long identityToken = clearCallingIdentity();
- try {
- SyncStorageEngine.EndPoint info =
- new SyncStorageEngine.EndPoint(account, authority, userId);
- getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
- defaultFlex, extras);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public void removePeriodicSync(Account account, String authority, Bundle extras) {
- Bundle.setDefusable(extras, true);
- if (account == null) {
- throw new IllegalArgumentException("Account must not be null");
- }
- if (TextUtils.isEmpty(authority)) {
- throw new IllegalArgumentException("Authority must not be empty");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- getSyncManager()
- .removePeriodicSync(
- new SyncStorageEngine.EndPoint(account, authority, userId),
- extras);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
- ComponentName cname) {
- if (account == null) {
- throw new IllegalArgumentException("Account must not be null");
- }
- if (TextUtils.isEmpty(providerName)) {
- throw new IllegalArgumentException("Authority must not be empty");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
- "no permission to read the sync settings");
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- return getSyncManager().getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(account, providerName, userId));
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public int getIsSyncable(Account account, String providerName) {
- return getIsSyncableAsUser(account, providerName, UserHandle.getCallingUserId());
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public int getIsSyncableAsUser(Account account, String providerName, int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read the sync settings for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
- "no permission to read the sync settings");
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getIsSyncable(
- account, userId, providerName);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- return -1;
- }
- @Override
- public void setIsSyncable(Account account, String providerName, int syncable) {
- if (TextUtils.isEmpty(providerName)) {
- throw new IllegalArgumentException("Authority must not be empty");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.getSyncStorageEngine().setIsSyncable(
- account, userId, providerName, syncable);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public boolean getMasterSyncAutomatically() {
- return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public boolean getMasterSyncAutomaticallyAsUser(int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read the sync settings for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
- "no permission to read the sync settings");
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- return false;
- }
- @Override
- public void setMasterSyncAutomatically(boolean flag) {
- setMasterSyncAutomaticallyAsUser(flag, UserHandle.getCallingUserId());
- }
- @Override
- public void setMasterSyncAutomaticallyAsUser(boolean flag, int userId) {
- enforceCrossUserPermission(userId,
- "no permission to set the sync status for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public boolean isSyncActive(Account account, String authority, ComponentName cname) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
- "no permission to read the sync stats");
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager == null) {
- return false;
- }
- return syncManager.getSyncStorageEngine().isSyncActive(
- new SyncStorageEngine.EndPoint(account, authority, userId));
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public List<SyncInfo> getCurrentSyncs() {
- return getCurrentSyncsAsUser(UserHandle.getCallingUserId());
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public List<SyncInfo> getCurrentSyncsAsUser(int userId) {
- enforceCrossUserPermission(userId,
- "no permission to read the sync settings for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
- "no permission to read the sync stats");
- final boolean canAccessAccounts =
- mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
- == PackageManager.PERMISSION_GRANTED;
- long identityToken = clearCallingIdentity();
- try {
- return getSyncManager().getSyncStorageEngine()
- .getCurrentSyncsCopy(userId, canAccessAccounts);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
- return getSyncStatusAsUser(account, authority, cname, UserHandle.getCallingUserId());
- }
- /**
- * If the user id supplied is different to the calling user, the caller must hold the
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Override
- public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
- ComponentName cname, int userId) {
- if (TextUtils.isEmpty(authority)) {
- throw new IllegalArgumentException("Authority must not be empty");
- }
- enforceCrossUserPermission(userId,
- "no permission to read the sync stats for user " + userId);
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
- "no permission to read the sync stats");
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager == null) {
- return null;
- }
- SyncStorageEngine.EndPoint info;
- if (!(account == null || authority == null)) {
- info = new SyncStorageEngine.EndPoint(account, authority, userId);
- } else {
- throw new IllegalArgumentException("Must call sync status with valid authority");
- }
- return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public boolean isSyncPending(Account account, String authority, ComponentName cname) {
- return isSyncPendingAsUser(account, authority, cname, UserHandle.getCallingUserId());
- }
- @Override
- public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
- int userId) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
- "no permission to read the sync stats");
- enforceCrossUserPermission(userId,
- "no permission to retrieve the sync settings for user " + userId);
- long identityToken = clearCallingIdentity();
- SyncManager syncManager = getSyncManager();
- if (syncManager == null) return false;
- try {
- SyncStorageEngine.EndPoint info;
- if (!(account == null || authority == null)) {
- info = new SyncStorageEngine.EndPoint(account, authority, userId);
- } else {
- throw new IllegalArgumentException("Invalid authority specified");
- }
- return syncManager.getSyncStorageEngine().isSyncPending(info);
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null && callback != null) {
- syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- @Override
- public void removeStatusChangeListener(ISyncStatusObserver callback) {
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null && callback != null) {
- syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
- private @Nullable String getProviderPackageName(Uri uri) {
- final ProviderInfo pi = mContext.getPackageManager()
- .resolveContentProvider(uri.getAuthority(), 0);
- return (pi != null) ? pi.packageName : null;
- }
- private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
- String providerPackageName) {
- ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
- if (userCache == null) {
- userCache = new ArrayMap<>();
- mCache.put(userId, userCache);
- }
- ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
- if (packageCache == null) {
- packageCache = new ArrayMap<>();
- userCache.put(providerPackageName, packageCache);
- }
- return packageCache;
- }
- private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
- ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
- if (userCache == null) return;
- ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
- if (packageCache == null) return;
- if (uri != null) {
- for (int i = 0; i < packageCache.size();) {
- final Pair<String, Uri> key = packageCache.keyAt(i);
- if (key.second != null && key.second.toString().startsWith(uri.toString())) {
- if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
- packageCache.removeAt(i);
- } else {
- i++;
- }
- }
- } else {
- if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
- packageCache.clear();
- }
- }
- @Override
- public void putCache(String packageName, Uri key, Bundle value, int userId) {
- Bundle.setDefusable(value, true);
- enforceCrossUserPermission(userId, TAG);
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
- mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
- packageName);
- final String providerPackageName = getProviderPackageName(key);
- final Pair<String, Uri> fullKey = Pair.create(packageName, key);
- synchronized (mCache) {
- final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
- providerPackageName);
- if (value != null) {
- cache.put(fullKey, value);
- } else {
- cache.remove(fullKey);
- }
- }
- }
- @Override
- public Bundle getCache(String packageName, Uri key, int userId) {
- enforceCrossUserPermission(userId, TAG);
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
- mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
- packageName);
- final String providerPackageName = getProviderPackageName(key);
- final Pair<String, Uri> fullKey = Pair.create(packageName, key);
- synchronized (mCache) {
- final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
- providerPackageName);
- return cache.get(fullKey);
- }
- }
- /**
- * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
- * permission, if the userHandle is not for the caller.
- *
- * @param userHandle the user handle of the user we want to act on behalf of.
- * @param message the message to log on security exception.
- */
- private void enforceCrossUserPermission(int userHandle, String message) {
- final int callingUser = UserHandle.getCallingUserId();
- if (callingUser != userHandle) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- }
- }
- /**
- * Hide this class since it is not part of api,
- * but current unittest framework requires it to be public
- * @hide
- */
- public static final class ObserverNode {
- private class ObserverEntry implements IBinder.DeathRecipient {
- public final IContentObserver observer;
- public final int uid;
- public final int pid;
- public final boolean notifyForDescendants;
- private final int userHandle;
- private final Object observersLock;
- public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
- int _uid, int _pid, int _userHandle) {
- this.observersLock = observersLock;
- observer = o;
- uid = _uid;
- pid = _pid;
- userHandle = _userHandle;
- notifyForDescendants = n;
- try {
- observer.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
- @Override
- public void binderDied() {
- synchronized (observersLock) {
- removeObserverLocked(observer);
- }
- }
- public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- String name, String prefix, SparseIntArray pidCounts) {
- pidCounts.put(pid, pidCounts.get(pid)+1);
- pw.print(prefix); pw.print(name); pw.print(": pid=");
- pw.print(pid); pw.print(" uid=");
- pw.print(uid); pw.print(" user=");
- pw.print(userHandle); pw.print(" target=");
- pw.println(Integer.toHexString(System.identityHashCode(
- observer != null ? observer.asBinder() : null)));
- }
- }
- public static final int INSERT_TYPE = 0;
- public static final int UPDATE_TYPE = 1;
- public static final int DELETE_TYPE = 2;