Loading core/java/android/content/pm/CrossProfileApps.java +26 −3 Original line number Diff line number Diff line Loading @@ -268,15 +268,17 @@ public class CrossProfileApps { } /** * Returns whether the calling package can request user consent to interact across profiles. * Returns whether the calling package can request to navigate the user to * the relevant settings page to request user consent to interact across profiles. * * <p>If {@code true}, user consent can be obtained via {@link * <p>If {@code true}, the navigation intent can be obtained via {@link * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for * the calling user.</li> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * <li>The calling package has either been whitelisted by default by the OEM or has been Loading @@ -285,6 +287,10 @@ public class CrossProfileApps { * </li> * </ul> * * <p>Note that in order for the user to be able to grant the consent, the requesting package * must be whitelisted by the admin or the OEM and installed in the other profile. If this is * not the case the user will be shown a message explaining why they can't grant the consent. * * <p>Note that user consent could already be granted if given a return value of {@code true}. * The package's current ability to interact across profiles can be checked with {@link * #canInteractAcrossProfiles()}. Loading Loading @@ -421,6 +427,23 @@ public class CrossProfileApps { } } /** * Returns {@code true} if the given package has requested * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one * other profile in the same profile group. * * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will * not return {@code false} if the app is not whitelisted or not installed in the other profile. * * @hide */ public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { try { return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission Loading core/java/android/content/pm/ICrossProfileApps.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -38,5 +38,6 @@ interface ICrossProfileApps { boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); } services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +16 −8 Original line number Diff line number Diff line Loading @@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); if (enabledProfileIds.length < 2) { return false; } return isCrossProfilePackageWhitelisted(packageName); return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { Loading Loading @@ -540,6 +537,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return isCrossProfilePackageWhitelisted(packageName); } @Override public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { final int[] profileIds = mInjector.getUserManager().getProfileIds( mInjector.getCallingUserId(), /* enabledOnly= */ false); if (profileIds.length < 2) { return false; } return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = Loading services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,38 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() { mockUninstallCrossProfileAppFromWorkProfile(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() throws Exception { mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isFalse(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() { mockCrossProfileAppNotWhitelisted(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } Loading Loading
core/java/android/content/pm/CrossProfileApps.java +26 −3 Original line number Diff line number Diff line Loading @@ -268,15 +268,17 @@ public class CrossProfileApps { } /** * Returns whether the calling package can request user consent to interact across profiles. * Returns whether the calling package can request to navigate the user to * the relevant settings page to request user consent to interact across profiles. * * <p>If {@code true}, user consent can be obtained via {@link * <p>If {@code true}, the navigation intent can be obtained via {@link * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for * the calling user.</li> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * <li>The calling package has either been whitelisted by default by the OEM or has been Loading @@ -285,6 +287,10 @@ public class CrossProfileApps { * </li> * </ul> * * <p>Note that in order for the user to be able to grant the consent, the requesting package * must be whitelisted by the admin or the OEM and installed in the other profile. If this is * not the case the user will be shown a message explaining why they can't grant the consent. * * <p>Note that user consent could already be granted if given a return value of {@code true}. * The package's current ability to interact across profiles can be checked with {@link * #canInteractAcrossProfiles()}. Loading Loading @@ -421,6 +427,23 @@ public class CrossProfileApps { } } /** * Returns {@code true} if the given package has requested * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one * other profile in the same profile group. * * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will * not return {@code false} if the app is not whitelisted or not installed in the other profile. * * @hide */ public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { try { return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission Loading
core/java/android/content/pm/ICrossProfileApps.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -38,5 +38,6 @@ interface ICrossProfileApps { boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); }
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +16 −8 Original line number Diff line number Diff line Loading @@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); if (enabledProfileIds.length < 2) { return false; } return isCrossProfilePackageWhitelisted(packageName); return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { Loading Loading @@ -540,6 +537,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return isCrossProfilePackageWhitelisted(packageName); } @Override public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { final int[] profileIds = mInjector.getUserManager().getProfileIds( mInjector.getCallingUserId(), /* enabledOnly= */ false); if (profileIds.length < 2) { return false; } return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = Loading
services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,38 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() { mockUninstallCrossProfileAppFromWorkProfile(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() throws Exception { mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isFalse(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() { mockCrossProfileAppNotWhitelisted(); assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } @Test public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) .isTrue(); } private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } Loading