Merge "Add CrossProfileApps#startActivity which takes an Intent."

This commit is contained in:
Alex Kershaw
2020-01-22 07:37:03 +00:00
committed by Android (Google) Code Review
4 changed files with 99 additions and 0 deletions

View File

@@ -11490,6 +11490,7 @@ package android.content.pm {
method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle);
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
}

View File

@@ -93,6 +93,32 @@ public class CrossProfileApps {
}
}
/**
* Starts the specified activity of the caller package in the specified profile.
*
* <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
* permission and both the caller and target user profiles must be in the same profile group.
*
* @param intent The intent to launch. A component in the caller package must be specified.
* @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
* {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
* {@link SecurityException} will be thrown.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_PROFILES,
android.Manifest.permission.INTERACT_ACROSS_USERS})
public void startActivity(@NonNull Intent intent, @NonNull UserHandle targetUser) {
try {
mService.startActivityAsUserByIntent(
mContext.getIApplicationThread(),
mContext.getPackageName(),
intent,
targetUser.getIdentifier());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Starts the specified activity of the caller package in the specified profile. Unlike
* {@link #startMainActivity}, this can start any activity of the caller package, not just

View File

@@ -29,6 +29,8 @@ import android.os.UserHandle;
interface ICrossProfileApps {
void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
in ComponentName component, int userId, boolean launchMainActivity);
void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
in Intent intent, int userId);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
boolean canInteractAcrossProfiles(in String callingPackage);
boolean canRequestInteractAcrossProfiles(in String callingPackage);

View File

@@ -172,6 +172,50 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
userId);
}
@Override
public void startActivityAsUserByIntent(
IApplicationThread caller,
String callingPackage,
Intent intent,
@UserIdInt int userId) throws RemoteException {
Objects.requireNonNull(callingPackage);
Objects.requireNonNull(intent);
Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set");
verifyCallingPackage(callingPackage);
final int callerUserId = mInjector.getCallingUserId();
final int callingUid = mInjector.getCallingUid();
List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
callingPackage, callerUserId);
if (callerUserId != userId && !allowedTargetUsers.contains(UserHandle.of(userId))) {
throw new SecurityException(callingPackage + " cannot access unrelated user " + userId);
}
Intent launchIntent = new Intent(intent);
launchIntent.setPackage(callingPackage);
if (!callingPackage.equals(launchIntent.getComponent().getPackageName())) {
throw new SecurityException(
callingPackage + " attempts to start an activity in other package - "
+ launchIntent.getComponent().getPackageName());
}
if (callerUserId != userId) {
if (!hasInteractAcrossProfilesPermission(callingPackage)) {
throw new SecurityException("Attempt to launch activity without required "
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+ " or target user is not in the same profile group.");
}
}
verifyActivityCanHandleIntent(launchIntent, callingUid, userId);
mInjector.getActivityTaskManagerInternal().startActivityAsUser(
caller, callingPackage, launchIntent, /* options= */ null, userId);
}
@Override
public boolean canRequestInteractAcrossProfiles(String callingPackage) {
Objects.requireNonNull(callingPackage);
@@ -214,6 +258,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
if (targetUserProfiles.isEmpty()) {
return false;
}
return hasInteractAcrossProfilesPermission(callingPackage);
}
private boolean hasInteractAcrossProfilesPermission(String callingPackage) {
final int callingUid = mInjector.getCallingUid();
final int callingPid = mInjector.getCallingPid();
return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
@@ -275,6 +324,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
}
}
private void verifyActivityCanHandleIntent(
Intent launchIntent, int callingUid, @UserIdInt int userId) {
final long ident = mInjector.clearCallingIdentity();
try {
final List<ResolveInfo> activities =
mInjector.getPackageManagerInternal().queryIntentActivities(
launchIntent,
launchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
callingUid,
userId);
if (!activities.isEmpty()) {
return;
}
throw new SecurityException("Activity cannot handle intent");
} finally {
mInjector.restoreCallingIdentity(ident);
}
}
/**
* Verify that the specified intent does resolved to the specified component and the resolved
* activity is exported.