Loading core/java/android/content/pm/CrossProfileApps.java +26 −3 Original line number Original line 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 * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * * * <p>Specifically, returns whether the following are all true: * <p>Specifically, returns whether the following are all true: * <ul> * <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> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * {@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 * <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> * </li> * </ul> * </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}. * <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 * The package's current ability to interact across profiles can be checked with {@link * #canInteractAcrossProfiles()}. * #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 * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission Loading core/java/android/content/pm/ICrossProfileApps.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -38,5 +38,6 @@ interface ICrossProfileApps { boolean canRequestInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); } } services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +16 −8 Original line number Original line Diff line number Diff line Loading @@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { List<UserHandle> targetUserProfiles = final int[] enabledProfileIds = getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { if (enabledProfileIds.length < 2) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { return false; return false; } } return isCrossProfilePackageWhitelisted(packageName); return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } } private boolean hasRequestedAppOpPermission(String permission, String packageName) { private boolean hasRequestedAppOpPermission(String permission, String packageName) { Loading Loading @@ -540,6 +537,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return isCrossProfilePackageWhitelisted(packageName); 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) { private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = final int[] profileIds = Loading services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +32 −0 Original line number Original line Diff line number Diff line Loading @@ -419,6 +419,38 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); .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) { private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } } Loading Loading
core/java/android/content/pm/CrossProfileApps.java +26 −3 Original line number Original line 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 * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * * * <p>Specifically, returns whether the following are all true: * <p>Specifically, returns whether the following are all true: * <ul> * <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> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * {@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 * <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> * </li> * </ul> * </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}. * <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 * The package's current ability to interact across profiles can be checked with {@link * #canInteractAcrossProfiles()}. * #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 * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission Loading
core/java/android/content/pm/ICrossProfileApps.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -38,5 +38,6 @@ interface ICrossProfileApps { boolean canRequestInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); } }
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +16 −8 Original line number Original line Diff line number Diff line Loading @@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { List<UserHandle> targetUserProfiles = final int[] enabledProfileIds = getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { if (enabledProfileIds.length < 2) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { return false; return false; } } return isCrossProfilePackageWhitelisted(packageName); return hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } } private boolean hasRequestedAppOpPermission(String permission, String packageName) { private boolean hasRequestedAppOpPermission(String permission, String packageName) { Loading Loading @@ -540,6 +537,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return isCrossProfilePackageWhitelisted(packageName); 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) { private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = final int[] profileIds = Loading
services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +32 −0 Original line number Original line Diff line number Diff line Loading @@ -419,6 +419,38 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); .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) { private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } } Loading