Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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"; } core/java/android/content/pm/CrossProfileApps.java +26 −0 Original line number Diff line number Diff line Loading @@ -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 Loading core/java/android/content/pm/ICrossProfileApps.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +70 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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. Loading Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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"; }
core/java/android/content/pm/CrossProfileApps.java +26 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/content/pm/ICrossProfileApps.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +70 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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. Loading