diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 179fc5c661a5f..6ba811e077ac8 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -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. * - *

If {@code true}, user consent can be obtained via {@link + *

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. * *

Specifically, returns whether the following are all true: *

* + *

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. + * *

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()}. @@ -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. + * + *

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 diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 4cecb30990e6c..9b0dae221538b 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -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 packageNames); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 83da38195053a..7069818e38941 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { - List targetUserProfiles = - getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); - if (targetUserProfiles.isEmpty()) { + final int[] enabledProfileIds = + mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); + if (enabledProfileIds.length < 2) { return false; } - if (!hasRequestedAppOpPermission( - AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { - return false; - } - return isCrossProfilePackageWhitelisted(packageName); + return hasRequestedAppOpPermission( + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { @@ -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 = diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index fa0febd7f20f1..acdb681421785 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -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); }