Expose per-user APIs for content services.

Bug: 15466880
Change-Id: Ib5a030e78559307627fe0d2e80ce6f1a7825109d
This commit is contained in:
Alexandra Gherghina
2014-06-23 13:34:59 +01:00
parent 13a11d8ead
commit 0363c3eb08
6 changed files with 251 additions and 18 deletions

View File

@@ -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);
}

View File

@@ -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
* <p>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.
* <p>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.
* <p>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<SyncInfo> 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.
* <p>This method requires the caller to hold the permission

View File

@@ -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<SyncInfo> getCurrentSyncs();
List<SyncInfo> 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.

View File

@@ -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.
*

View File

@@ -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.
*
* <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");
}
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.
*
* <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 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<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.
*/
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");
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

View File

@@ -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<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
throw new UnsupportedOperationException();