diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 13b922cd890cc..cb48e5885207f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -621,10 +621,15 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public ProviderInfo resolveContentProvider(String name, - int flags) { + public ProviderInfo resolveContentProvider(String name, int flags) { + return resolveContentProviderAsUser(name, flags, mContext.getUserId()); + } + + /** @hide **/ + @Override + public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) { try { - return mPM.resolveContentProvider(name, flags, mContext.getUserId()); + return mPM.resolveContentProvider(name, flags, userId); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 392bfbc0b942a..be70411e56ef2 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1751,6 +1751,15 @@ public abstract class ContentResolver { * @param extras any extras to pass to the SyncAdapter. */ public static void requestSync(Account account, String authority, Bundle extras) { + requestSyncAsUser(account, authority, UserHandle.getCallingUserId(), extras); + } + + /** + * @see #requestSync(Account, String, Bundle) + * @hide + */ + public static void requestSyncAsUser(Account account, String authority, int userId, + Bundle extras) { if (extras == null) { throw new IllegalArgumentException("Must specify extras."); } @@ -1760,7 +1769,11 @@ public abstract class ContentResolver { .setExtras(extras) .syncOnce() // Immediate sync. .build(); - requestSync(request); + try { + getContentService().syncAsUser(request, userId); + } catch(RemoteException e) { + // Shouldn't happen. + } } /** @@ -1838,6 +1851,17 @@ public abstract class ContentResolver { } } + /** + * @see #cancelSync(Account, String) + * @hide + */ + public static void cancelSyncAsUser(Account account, String authority, int userId) { + try { + getContentService().cancelSyncAsUser(account, authority, null, userId); + } catch (RemoteException e) { + } + } + /** * Get information about the SyncAdapters that are known to the system. * @return an array of SyncAdapters that have registered with the system @@ -1850,6 +1874,18 @@ public abstract class ContentResolver { } } + /** + * @see #getSyncAdapterTypes() + * @hide + */ + public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) { + try { + return getContentService().getSyncAdapterTypesAsUser(userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Check if the provider should be synced when a network tickle is received *

This method requires the caller to hold the permission @@ -1867,6 +1903,19 @@ public abstract class ContentResolver { } } + /** + * @see #getSyncAutomatically(Account, String) + * @hide + */ + public static boolean getSyncAutomaticallyAsUser(Account account, String authority, + int userId) { + try { + return getContentService().getSyncAutomaticallyAsUser(account, authority, userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Set whether or not the provider is synced when it receives a network tickle. *

This method requires the caller to hold the permission @@ -2031,6 +2080,18 @@ public abstract class ContentResolver { } } + /** + * @see #getIsSyncable(Account, String) + * @hide + */ + public static int getIsSyncableAsUser(Account account, String authority, int userId) { + try { + return getContentService().getIsSyncableAsUser(account, authority, userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Set whether this account/provider is syncable. *

This method requires the caller to hold the permission @@ -2062,6 +2123,18 @@ public abstract class ContentResolver { } } + /** + * @see #getMasterSyncAutomatically() + * @hide + */ + public static boolean getMasterSyncAutomaticallyAsUser(int userId) { + try { + return getContentService().getMasterSyncAutomaticallyAsUser(userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Sets the master auto-sync setting that applies to all the providers and accounts. * If this is false then the per-provider auto-sync setting is ignored. @@ -2146,6 +2219,18 @@ public abstract class ContentResolver { } } + /** + * @see #getCurrentSyncs() + * @hide + */ + public static List getCurrentSyncsAsUser(int userId) { + try { + return getContentService().getCurrentSyncsAsUser(userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Returns the status that matches the authority. * @param account the account whose setting we are querying @@ -2161,6 +2246,19 @@ public abstract class ContentResolver { } } + /** + * @see #getSyncStatus(Account, String) + * @hide + */ + public static SyncStatusInfo getSyncStatusAsUser(Account account, String authority, + int userId) { + try { + return getContentService().getSyncStatusAsUser(account, authority, null, userId); + } catch (RemoteException e) { + throw new RuntimeException("the ContentService should always be reachable", e); + } + } + /** * Return true if the pending status is true of any matching authorities. *

This method requires the caller to hold the permission diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 373f2fb624947..1e967134f42a0 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -60,7 +60,9 @@ interface IContentService { * Start a sync given a request. */ void sync(in SyncRequest request); + void syncAsUser(in SyncRequest request, int userId); void cancelSync(in Account account, String authority, in ComponentName cname); + void cancelSyncAsUser(in Account account, String authority, in ComponentName cname, int userId); /** Cancel a sync, providing information about the sync to be cancelled. */ void cancelRequest(in SyncRequest request); @@ -71,6 +73,7 @@ interface IContentService { * @return true if the provider should be synced when a network tickle is received */ boolean getSyncAutomatically(in Account account, String providerName); + boolean getSyncAutomaticallyAsUser(in Account account, String providerName, int userId); /** * Set whether or not the provider is synced when it receives a network tickle. @@ -114,6 +117,7 @@ interface IContentService { * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet. */ int getIsSyncable(in Account account, String providerName); + int getIsSyncableAsUser(in Account account, String providerName, int userId); /** * Set whether this account/provider is syncable. @@ -124,14 +128,17 @@ interface IContentService { void setMasterSyncAutomatically(boolean flag); boolean getMasterSyncAutomatically(); + boolean getMasterSyncAutomaticallyAsUser(int userId); List getCurrentSyncs(); + List getCurrentSyncsAsUser(int userId); /** * Returns the types of the SyncAdapters that are registered with the system. * @return Returns the types of the SyncAdapters that are registered with the system. */ SyncAdapterType[] getSyncAdapterTypes(); + SyncAdapterType[] getSyncAdapterTypesAsUser(int userId); /** * Returns true if there is currently a operation for the given account/authority or service @@ -152,6 +159,8 @@ interface IContentService { * non-null. */ SyncStatusInfo getSyncStatus(in Account account, String authority, in ComponentName cname); + SyncStatusInfo getSyncStatusAsUser(in Account account, String authority, in ComponentName cname, + int userId); /** * Return true if the pending status is true of any matching authorities. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 720315d363ec1..9ac433f0e8d76 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2477,6 +2477,19 @@ public abstract class PackageManager { public abstract ProviderInfo resolveContentProvider(String name, int flags); + /** + * Find a single content provider by its base path name. + * + * @param name The name of the provider to find. + * @param flags Additional option flags. Currently should always be 0. + * @param userId The user id. + * + * @return ContentProviderInfo Information about the provider, if found, + * else null. + * @hide + */ + public abstract ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId); + /** * Retrieve content provider information. * diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 3b55bfc3b8e9f..a6485e99599b3 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -172,11 +172,8 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } - final int callingUser = UserHandle.getCallingUserId(); - if (callingUser != userHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "no permission to observe other users' provider view"); - } + enforceCrossUserPermission(userHandle, + "no permission to observe other users' provider view"); if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { @@ -346,7 +343,15 @@ public final class ContentService extends IContentService.Stub { * @param request The request object. Validation of this object is done by its builder. */ public void sync(SyncRequest request) { - int userId = UserHandle.getCallingUserId(); + syncAsUser(request, UserHandle.getCallingUserId()); + } + + /** + * If the user id supplied is different to the calling user, the caller must hold the + * INTERACT_ACROSS_USERS_FULL permission. + */ + 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. @@ -399,11 +404,30 @@ public final class ContentService extends IContentService.Stub { */ @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. + * + *

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"); } - - int userId = UserHandle.getCallingUserId(); + 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(); @@ -456,9 +480,23 @@ public final class ContentService extends IContentService.Stub { */ @Override public SyncAdapterType[] getSyncAdapterTypes() { + return getSyncAdapterTypesAsUser(UserHandle.getCallingUserId()); + } + + /** + * Get information about the SyncAdapters that are known to the system for a particular user. + * + *

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 int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); @@ -470,10 +508,20 @@ public final class ContentService extends IContentService.Stub { @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"); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); @@ -588,9 +636,18 @@ public final class ContentService extends IContentService.Stub { } 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. + */ + 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"); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { @@ -627,10 +684,20 @@ public final class ContentService extends IContentService.Stub { @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"); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); @@ -679,10 +746,19 @@ public final class ContentService extends IContentService.Stub { } public List 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. + */ + public List 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"); - int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId); @@ -692,13 +768,24 @@ public final class ContentService extends IContentService.Stub { } 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. + */ + 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"); - int userId = UserHandle.getCallingUserId(); int callerUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { @@ -771,6 +858,21 @@ public final class ContentService extends IContentService.Stub { return service; } + /** + * 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 diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 1d107296a80e1..59036daea83bb 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -305,6 +305,12 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) { + throw new UnsupportedOperationException(); + } + @Override public List queryContentProviders(String processName, int uid, int flags) { throw new UnsupportedOperationException();