Loading core/java/android/content/pm/CrossProfileApps.java +20 −6 Original line number Original line Diff line number Diff line Loading @@ -316,14 +316,21 @@ public class CrossProfileApps { * * * <p>If other changes could have affected the app's ability to interact across profiles, as * <p>If other changes could have affected the app's ability to interact across profiles, as * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * admin or OEM consent whitelists, then {@link * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection, * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. * Set)} should be used. * * <p>If the caller does not have the {@link android.Manifest.permission * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. * * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. * * * @hide * @hide */ */ @RequiresPermission( @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) android.Manifest.permission.INTERACT_ACROSS_USERS}) public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { try { try { Loading Loading @@ -360,11 +367,18 @@ public class CrossProfileApps { * have changed as a result of non-user actions, such as changes to admin or OEM consent * have changed as a result of non-user actions, such as changes to admin or OEM consent * whitelists. * whitelists. * * * <p>If the caller does not have the {@link android.Manifest.permission * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. * * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. * * @hide * @hide */ */ @RequiresPermission( @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, @NonNull Collection<String> previousCrossProfilePackages, Loading core/res/AndroidManifest.xml +6 −0 Original line number Original line Diff line number Diff line Loading @@ -2392,6 +2392,12 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop|documenter|wellbeing" /> android:protectionLevel="signature|appop|documenter|wellbeing" /> <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can interact across profiles in the same profile group. @hide --> <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES" android:protectionLevel="signature" /> <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage users on the device. This permission is not available to users on the device. This permission is not available to third party applications. --> third party applications. --> Loading data/etc/privapp-permissions-platform.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ applications that come with the platform <privapp-permissions package="com.android.managedprovisioning"> <privapp-permissions package="com.android.managedprovisioning"> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> <permission name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"/> <permission name="android.permission.CRYPT_KEEPER"/> <permission name="android.permission.CRYPT_KEEPER"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> Loading services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +72 −53 Original line number Original line Diff line number Diff line Loading @@ -56,6 +56,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; Loading Loading @@ -276,19 +278,14 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean isCrossProfilePackageWhitelisted(String packageName) { private boolean isCrossProfilePackageWhitelisted(String packageName) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> try { mInjector.getDevicePolicyManagerInternal() return mInjector.getDevicePolicyManagerInternal() .getAllCrossProfilePackages().contains(packageName)); .getAllCrossProfilePackages().contains(packageName); } finally { mInjector.restoreCallingIdentity(ident); } } } private List<UserHandle> getTargetUserProfilesUnchecked( private List<UserHandle> getTargetUserProfilesUnchecked( String packageName, @UserIdInt int userId) { String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final int[] enabledProfileIds = final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(userId); mInjector.getUserManager().getEnabledProfileIds(userId); Loading @@ -303,15 +300,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { targetProfiles.add(UserHandle.of(profileId)); targetProfiles.add(UserHandle.of(profileId)); } } return targetProfiles; return targetProfiles; } finally { }); mInjector.restoreCallingIdentity(ident); } } } private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); final int callingUid = mInjector.getCallingUid(); final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final PackageInfo info = mInjector.getPackageManagerInternal() final PackageInfo info = mInjector.getPackageManagerInternal() .getPackageInfo( .getPackageInfo( packageName, packageName, Loading @@ -319,15 +313,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, callingUid, userId); userId); return info != null && info.applicationInfo.enabled; return info != null && info.applicationInfo.enabled; } finally { }); mInjector.restoreCallingIdentity(ident); } } } private void verifyActivityCanHandleIntent( private void verifyActivityCanHandleIntent( Intent launchIntent, int callingUid, @UserIdInt int userId) { Intent launchIntent, int callingUid, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); mInjector.withCleanCallingIdentity(() -> { try { final List<ResolveInfo> activities = final List<ResolveInfo> activities = mInjector.getPackageManagerInternal().queryIntentActivities( mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, launchIntent, Loading @@ -340,9 +331,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; return; } } throw new SecurityException("Activity cannot handle intent"); throw new SecurityException("Activity cannot handle intent"); } finally { }); mInjector.restoreCallingIdentity(ident); } } } /** /** Loading @@ -351,8 +340,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { */ */ private void verifyActivityCanHandleIntentAndExported( private void verifyActivityCanHandleIntentAndExported( Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) { Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); mInjector.withCleanCallingIdentity(() -> { try { final List<ResolveInfo> apps = final List<ResolveInfo> apps = mInjector.getPackageManagerInternal().queryIntentActivities( mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, launchIntent, Loading @@ -371,9 +359,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } throw new SecurityException("Attempt to launch activity without " throw new SecurityException("Attempt to launch activity without " + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); } finally { }); mInjector.restoreCallingIdentity(ident); } } } @Override @Override Loading @@ -385,7 +371,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" + " app-op for interacting across profiles."); + " app-op for interacting across profiles."); } } final int callingUserId = mInjector.getCallingUserId(); if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid) && !isPermissionGranted( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { throw new SecurityException( "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set" + " the app-op for interacting across profiles."); } if (newMode == AppOpsManager.MODE_ALLOWED if (newMode == AppOpsManager.MODE_ALLOWED && !canConfigureInteractAcrossProfiles(packageName)) { && !canConfigureInteractAcrossProfiles(packageName)) { // The user should not be prompted for apps that cannot request to interact across // The user should not be prompted for apps that cannot request to interact across Loading @@ -395,7 +387,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; return; } } final int[] profileIds = final int[] profileIds = mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false); mInjector.getUserManager() .getProfileIds(mInjector.getCallingUserId(), /* enabledOnly= */ false); for (int profileId : profileIds) { for (int profileId : profileIds) { if (!isPackageInstalled(packageName, profileId)) { if (!isPackageInstalled(packageName, profileId)) { continue; continue; Loading @@ -406,8 +399,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); final int callingUid = mInjector.getCallingUid(); final long identity = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final PackageInfo info = final PackageInfo info = mInjector.getPackageManagerInternal() mInjector.getPackageManagerInternal() .getPackageInfo( .getPackageInfo( Loading @@ -416,9 +408,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, callingUid, userId); userId); return info != null; return info != null; } finally { }); mInjector.restoreCallingIdentity(identity); } } } private void setInteractAcrossProfilesAppOpForUser( private void setInteractAcrossProfilesAppOpForUser( Loading @@ -440,19 +430,31 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { + packageName + " on user ID " + userId); + packageName + " on user ID " + userId); return; return; } } final int callingUid = mInjector.getCallingUid(); if (isPermissionGranted( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { // Clear calling identity since the CONFIGURE_INTERACT_ACROSS_PROFILES permission allows // this particular app-op to be modified without the broader app-op permissions. mInjector.withCleanCallingIdentity(() -> mInjector.getAppOpsManager() mInjector.getAppOpsManager() .setMode(OP_INTERACT_ACROSS_PROFILES, .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode)); uid, } else { packageName, mInjector.getAppOpsManager() newMode); .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode); } sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); } } /** * Returns whether the given app-op mode is equivalent to the currently-set app-op of the given * package name and UID. Clears identity to avoid permission checks, so ensure the caller does * any necessary permission checks. */ private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { final String op = final String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); return otherMode == return mInjector.withCleanCallingIdentity(() -> otherMode mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName); == mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName)); } } private void sendCanInteractAcrossProfilesChangedBroadcast( private void sendCanInteractAcrossProfilesChangedBroadcast( Loading Loading @@ -493,8 +495,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final int[] profileIds = final int[] profileIds = mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); for (int profileId : profileIds) { for (int profileId : profileIds) { Loading @@ -502,10 +503,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return true; return true; } } } } } finally { mInjector.restoreCallingIdentity(ident); } return false; return false; }); } } @Override @Override Loading @@ -525,12 +524,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> try { mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)); return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId); } finally { mInjector.restoreCallingIdentity(ident); } } } /** /** Loading Loading @@ -560,42 +555,62 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mContext = context; mContext = context; } } @Override public int getCallingUid() { public int getCallingUid() { return Binder.getCallingUid(); return Binder.getCallingUid(); } } @Override public int getCallingPid() { public int getCallingPid() { return Binder.getCallingPid(); return Binder.getCallingPid(); } } @Override public int getCallingUserId() { public int getCallingUserId() { return UserHandle.getCallingUserId(); return UserHandle.getCallingUserId(); } } @Override public UserHandle getCallingUserHandle() { public UserHandle getCallingUserHandle() { return Binder.getCallingUserHandle(); return Binder.getCallingUserHandle(); } } @Override public long clearCallingIdentity() { public long clearCallingIdentity() { return Binder.clearCallingIdentity(); return Binder.clearCallingIdentity(); } } @Override public void restoreCallingIdentity(long token) { public void restoreCallingIdentity(long token) { Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token); } } @Override public void withCleanCallingIdentity(ThrowingRunnable action) { Binder.withCleanCallingIdentity(action); } @Override public final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { return Binder.withCleanCallingIdentity(action); } @Override public UserManager getUserManager() { public UserManager getUserManager() { return mContext.getSystemService(UserManager.class); return mContext.getSystemService(UserManager.class); } } @Override public PackageManagerInternal getPackageManagerInternal() { public PackageManagerInternal getPackageManagerInternal() { return LocalServices.getService(PackageManagerInternal.class); return LocalServices.getService(PackageManagerInternal.class); } } @Override public PackageManager getPackageManager() { public PackageManager getPackageManager() { return mContext.getPackageManager(); return mContext.getPackageManager(); } } @Override public AppOpsManager getAppOpsManager() { public AppOpsManager getAppOpsManager() { return mContext.getSystemService(AppOpsManager.class); return mContext.getSystemService(AppOpsManager.class); } } Loading Loading @@ -646,6 +661,10 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { void restoreCallingIdentity(long token); void restoreCallingIdentity(long token); void withCleanCallingIdentity(ThrowingRunnable action); <T> T withCleanCallingIdentity(ThrowingSupplier<T> action); UserManager getUserManager(); UserManager getUserManager(); PackageManagerInternal getPackageManagerInternal(); PackageManagerInternal getPackageManagerInternal(); Loading services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +64 −2 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowUserManager; import com.android.server.testing.shadows.ShadowUserManager; Loading Loading @@ -190,6 +192,8 @@ public class CrossProfileAppsServiceImplRoboTest { public void grantPermissions() { public void grantPermissions() { grantPermissions( grantPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); Manifest.permission.INTERACT_ACROSS_USERS_FULL); } } Loading @@ -212,10 +216,27 @@ public class CrossProfileAppsServiceImplRoboTest { explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); } } @Test public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() { denyPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); try { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); fail(); } catch (SecurityException expected) {} } @Test @Test public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); denyPermissions( denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); try { try { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); Loading @@ -230,9 +251,39 @@ public class CrossProfileAppsServiceImplRoboTest { assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } } @Test public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() { denyPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS); grantPermissions( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } @Test public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() { denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); grantPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } @Test @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); Loading @@ -241,6 +292,7 @@ public class CrossProfileAppsServiceImplRoboTest { @Test @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); Loading Loading @@ -478,6 +530,16 @@ public class CrossProfileAppsServiceImplRoboTest { @Override @Override public void restoreCallingIdentity(long token) {} public void restoreCallingIdentity(long token) {} @Override public void withCleanCallingIdentity(ThrowingRunnable action) { action.run(); } @Override public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { return action.get(); } @Override @Override public UserManager getUserManager() { public UserManager getUserManager() { return mUserManager; return mUserManager; Loading Loading
core/java/android/content/pm/CrossProfileApps.java +20 −6 Original line number Original line Diff line number Diff line Loading @@ -316,14 +316,21 @@ public class CrossProfileApps { * * * <p>If other changes could have affected the app's ability to interact across profiles, as * <p>If other changes could have affected the app's ability to interact across profiles, as * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * admin or OEM consent whitelists, then {@link * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection, * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. * Set)} should be used. * * <p>If the caller does not have the {@link android.Manifest.permission * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. * * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. * * * @hide * @hide */ */ @RequiresPermission( @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) android.Manifest.permission.INTERACT_ACROSS_USERS}) public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { try { try { Loading Loading @@ -360,11 +367,18 @@ public class CrossProfileApps { * have changed as a result of non-user actions, such as changes to admin or OEM consent * have changed as a result of non-user actions, such as changes to admin or OEM consent * whitelists. * whitelists. * * * <p>If the caller does not have the {@link android.Manifest.permission * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. * * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. * * @hide * @hide */ */ @RequiresPermission( @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, @NonNull Collection<String> previousCrossProfilePackages, Loading
core/res/AndroidManifest.xml +6 −0 Original line number Original line Diff line number Diff line Loading @@ -2392,6 +2392,12 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop|documenter|wellbeing" /> android:protectionLevel="signature|appop|documenter|wellbeing" /> <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can interact across profiles in the same profile group. @hide --> <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES" android:protectionLevel="signature" /> <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage users on the device. This permission is not available to users on the device. This permission is not available to third party applications. --> third party applications. --> Loading
data/etc/privapp-permissions-platform.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,7 @@ applications that come with the platform <privapp-permissions package="com.android.managedprovisioning"> <privapp-permissions package="com.android.managedprovisioning"> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> <permission name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"/> <permission name="android.permission.CRYPT_KEEPER"/> <permission name="android.permission.CRYPT_KEEPER"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/> Loading
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +72 −53 Original line number Original line Diff line number Diff line Loading @@ -56,6 +56,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; Loading Loading @@ -276,19 +278,14 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean isCrossProfilePackageWhitelisted(String packageName) { private boolean isCrossProfilePackageWhitelisted(String packageName) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> try { mInjector.getDevicePolicyManagerInternal() return mInjector.getDevicePolicyManagerInternal() .getAllCrossProfilePackages().contains(packageName)); .getAllCrossProfilePackages().contains(packageName); } finally { mInjector.restoreCallingIdentity(ident); } } } private List<UserHandle> getTargetUserProfilesUnchecked( private List<UserHandle> getTargetUserProfilesUnchecked( String packageName, @UserIdInt int userId) { String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final int[] enabledProfileIds = final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(userId); mInjector.getUserManager().getEnabledProfileIds(userId); Loading @@ -303,15 +300,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { targetProfiles.add(UserHandle.of(profileId)); targetProfiles.add(UserHandle.of(profileId)); } } return targetProfiles; return targetProfiles; } finally { }); mInjector.restoreCallingIdentity(ident); } } } private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { private boolean isPackageEnabled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); final int callingUid = mInjector.getCallingUid(); final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final PackageInfo info = mInjector.getPackageManagerInternal() final PackageInfo info = mInjector.getPackageManagerInternal() .getPackageInfo( .getPackageInfo( packageName, packageName, Loading @@ -319,15 +313,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, callingUid, userId); userId); return info != null && info.applicationInfo.enabled; return info != null && info.applicationInfo.enabled; } finally { }); mInjector.restoreCallingIdentity(ident); } } } private void verifyActivityCanHandleIntent( private void verifyActivityCanHandleIntent( Intent launchIntent, int callingUid, @UserIdInt int userId) { Intent launchIntent, int callingUid, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); mInjector.withCleanCallingIdentity(() -> { try { final List<ResolveInfo> activities = final List<ResolveInfo> activities = mInjector.getPackageManagerInternal().queryIntentActivities( mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, launchIntent, Loading @@ -340,9 +331,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; return; } } throw new SecurityException("Activity cannot handle intent"); throw new SecurityException("Activity cannot handle intent"); } finally { }); mInjector.restoreCallingIdentity(ident); } } } /** /** Loading @@ -351,8 +340,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { */ */ private void verifyActivityCanHandleIntentAndExported( private void verifyActivityCanHandleIntentAndExported( Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) { Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); mInjector.withCleanCallingIdentity(() -> { try { final List<ResolveInfo> apps = final List<ResolveInfo> apps = mInjector.getPackageManagerInternal().queryIntentActivities( mInjector.getPackageManagerInternal().queryIntentActivities( launchIntent, launchIntent, Loading @@ -371,9 +359,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } throw new SecurityException("Attempt to launch activity without " throw new SecurityException("Attempt to launch activity without " + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component); } finally { }); mInjector.restoreCallingIdentity(ident); } } } @Override @Override Loading @@ -385,7 +371,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" + " app-op for interacting across profiles."); + " app-op for interacting across profiles."); } } final int callingUserId = mInjector.getCallingUserId(); if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid) && !isPermissionGranted( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { throw new SecurityException( "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set" + " the app-op for interacting across profiles."); } if (newMode == AppOpsManager.MODE_ALLOWED if (newMode == AppOpsManager.MODE_ALLOWED && !canConfigureInteractAcrossProfiles(packageName)) { && !canConfigureInteractAcrossProfiles(packageName)) { // The user should not be prompted for apps that cannot request to interact across // The user should not be prompted for apps that cannot request to interact across Loading @@ -395,7 +387,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return; return; } } final int[] profileIds = final int[] profileIds = mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false); mInjector.getUserManager() .getProfileIds(mInjector.getCallingUserId(), /* enabledOnly= */ false); for (int profileId : profileIds) { for (int profileId : profileIds) { if (!isPackageInstalled(packageName, profileId)) { if (!isPackageInstalled(packageName, profileId)) { continue; continue; Loading @@ -406,8 +399,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { final int callingUid = mInjector.getCallingUid(); final int callingUid = mInjector.getCallingUid(); final long identity = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final PackageInfo info = final PackageInfo info = mInjector.getPackageManagerInternal() mInjector.getPackageManagerInternal() .getPackageInfo( .getPackageInfo( Loading @@ -416,9 +408,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { callingUid, callingUid, userId); userId); return info != null; return info != null; } finally { }); mInjector.restoreCallingIdentity(identity); } } } private void setInteractAcrossProfilesAppOpForUser( private void setInteractAcrossProfilesAppOpForUser( Loading @@ -440,19 +430,31 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { + packageName + " on user ID " + userId); + packageName + " on user ID " + userId); return; return; } } final int callingUid = mInjector.getCallingUid(); if (isPermissionGranted( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) { // Clear calling identity since the CONFIGURE_INTERACT_ACROSS_PROFILES permission allows // this particular app-op to be modified without the broader app-op permissions. mInjector.withCleanCallingIdentity(() -> mInjector.getAppOpsManager() mInjector.getAppOpsManager() .setMode(OP_INTERACT_ACROSS_PROFILES, .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode)); uid, } else { packageName, mInjector.getAppOpsManager() newMode); .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode); } sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); } } /** * Returns whether the given app-op mode is equivalent to the currently-set app-op of the given * package name and UID. Clears identity to avoid permission checks, so ensure the caller does * any necessary permission checks. */ private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { final String op = final String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); return otherMode == return mInjector.withCleanCallingIdentity(() -> otherMode mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName); == mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName)); } } private void sendCanInteractAcrossProfilesChangedBroadcast( private void sendCanInteractAcrossProfilesChangedBroadcast( Loading Loading @@ -493,8 +495,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> { try { final int[] profileIds = final int[] profileIds = mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); for (int profileId : profileIds) { for (int profileId : profileIds) { Loading @@ -502,10 +503,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return true; return true; } } } } } finally { mInjector.restoreCallingIdentity(ident); } return false; return false; }); } } @Override @Override Loading @@ -525,12 +524,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); return mInjector.withCleanCallingIdentity(() -> try { mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)); return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId); } finally { mInjector.restoreCallingIdentity(ident); } } } /** /** Loading Loading @@ -560,42 +555,62 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mContext = context; mContext = context; } } @Override public int getCallingUid() { public int getCallingUid() { return Binder.getCallingUid(); return Binder.getCallingUid(); } } @Override public int getCallingPid() { public int getCallingPid() { return Binder.getCallingPid(); return Binder.getCallingPid(); } } @Override public int getCallingUserId() { public int getCallingUserId() { return UserHandle.getCallingUserId(); return UserHandle.getCallingUserId(); } } @Override public UserHandle getCallingUserHandle() { public UserHandle getCallingUserHandle() { return Binder.getCallingUserHandle(); return Binder.getCallingUserHandle(); } } @Override public long clearCallingIdentity() { public long clearCallingIdentity() { return Binder.clearCallingIdentity(); return Binder.clearCallingIdentity(); } } @Override public void restoreCallingIdentity(long token) { public void restoreCallingIdentity(long token) { Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token); } } @Override public void withCleanCallingIdentity(ThrowingRunnable action) { Binder.withCleanCallingIdentity(action); } @Override public final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { return Binder.withCleanCallingIdentity(action); } @Override public UserManager getUserManager() { public UserManager getUserManager() { return mContext.getSystemService(UserManager.class); return mContext.getSystemService(UserManager.class); } } @Override public PackageManagerInternal getPackageManagerInternal() { public PackageManagerInternal getPackageManagerInternal() { return LocalServices.getService(PackageManagerInternal.class); return LocalServices.getService(PackageManagerInternal.class); } } @Override public PackageManager getPackageManager() { public PackageManager getPackageManager() { return mContext.getPackageManager(); return mContext.getPackageManager(); } } @Override public AppOpsManager getAppOpsManager() { public AppOpsManager getAppOpsManager() { return mContext.getSystemService(AppOpsManager.class); return mContext.getSystemService(AppOpsManager.class); } } Loading Loading @@ -646,6 +661,10 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { void restoreCallingIdentity(long token); void restoreCallingIdentity(long token); void withCleanCallingIdentity(ThrowingRunnable action); <T> T withCleanCallingIdentity(ThrowingSupplier<T> action); UserManager getUserManager(); UserManager getUserManager(); PackageManagerInternal getPackageManagerInternal(); PackageManagerInternal getPackageManagerInternal(); Loading
services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +64 −2 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowUserManager; import com.android.server.testing.shadows.ShadowUserManager; Loading Loading @@ -190,6 +192,8 @@ public class CrossProfileAppsServiceImplRoboTest { public void grantPermissions() { public void grantPermissions() { grantPermissions( grantPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); Manifest.permission.INTERACT_ACROSS_USERS_FULL); } } Loading @@ -212,10 +216,27 @@ public class CrossProfileAppsServiceImplRoboTest { explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); } } @Test public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() { denyPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); try { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); fail(); } catch (SecurityException expected) {} } @Test @Test public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); denyPermissions( denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); try { try { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); Loading @@ -230,9 +251,39 @@ public class CrossProfileAppsServiceImplRoboTest { assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } } @Test public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() { denyPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS); grantPermissions( Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } @Test public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() { denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES); grantPermissions( Manifest.permission.MANAGE_APP_OPS_MODES, Manifest.permission.UPDATE_APP_OPS_STATS, Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); } @Test @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); Loading @@ -241,6 +292,7 @@ public class CrossProfileAppsServiceImplRoboTest { @Test @Test public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); Loading Loading @@ -478,6 +530,16 @@ public class CrossProfileAppsServiceImplRoboTest { @Override @Override public void restoreCallingIdentity(long token) {} public void restoreCallingIdentity(long token) {} @Override public void withCleanCallingIdentity(ThrowingRunnable action) { action.run(); } @Override public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { return action.get(); } @Override @Override public UserManager getUserManager() { public UserManager getUserManager() { return mUserManager; return mUserManager; Loading