diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/content/pm/CrossProfileApps.java | 26 | ||||
| -rw-r--r-- | core/java/android/content/pm/ICrossProfileApps.aidl | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java | 70 |
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. |