summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java26
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl2
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java70
4 files changed, 99 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt
index 54e1bad2a5b3..53a7d495aeb3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -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";
}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 5aa9c9bebc2c..48f473018127 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -94,6 +94,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
* the main activity.
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 694b1a3c4e73..755950cd5ebe 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -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);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 3635004987e8..fc08ddeddf82 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -173,6 +173,50 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
}
@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);
verifyCallingPackage(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.