summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Richard MacGregor <rmacgregor@google.com> 2025-02-06 08:28:42 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-06 08:28:42 -0800
commit7ca015743f6bbb7d64c7cc295848d54dcd0a1733 (patch)
tree416be3a3058bee42a31c70a0821d86d2d0adba7e
parent733cfd6367e2325486692461bc1d0a973ff81a7f (diff)
parent92fcb69765493138681fae331e0692f6b9f732d7 (diff)
Merge "Check DISALLOW_CONFIG_DEFAULT_APPS for all users of a cross-profile role" into main
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java39
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/IntentCompat.java25
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java28
-rw-r--r--service/java/com/android/permission/util/UserUtils.java2
-rw-r--r--tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt508
5 files changed, 591 insertions, 11 deletions
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
index 64682ba79..f0df97acc 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
@@ -47,6 +47,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.util.CollectionUtils;
+import com.android.role.controller.util.IntentCompat;
import com.android.role.controller.util.PackageUtils;
import com.android.role.controller.util.RoleFlags;
import com.android.role.controller.util.RoleManagerCompat;
@@ -1090,14 +1091,36 @@ public class Role {
@Nullable
public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) {
if (SdkLevel.isAtLeastU() && isExclusive()) {
- // TODO(b/379143953): if role is profile group exclusive
- // check DISALLOW_CONFIG_DEFAULT_APPS for all users
- UserManager userManager = context.getSystemService(UserManager.class);
- if (userManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_DEFAULT_APPS,
- user)) {
- return new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)
- .putExtra(DevicePolicyManager.EXTRA_RESTRICTION,
- UserManager.DISALLOW_CONFIG_DEFAULT_APPS);
+ boolean crossUserRoleUxBugfixEnabled =
+ com.android.permission.flags.Flags.crossUserRoleUxBugfixEnabled();
+ if (crossUserRoleUxBugfixEnabled && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) {
+ DevicePolicyManager devicePolicyManager =
+ context.getSystemService(DevicePolicyManager.class);
+ if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ // For profileGroup exclusive roles users on BYOD are free to choose personal or
+ // work profile app regardless of DISALLOW_CONFIG_DEFAULT_APPS
+ return null;
+ }
+ }
+
+ // Otherwise if role is profileGroup exclusive check DISALLOW_CONFIG_DEFAULT_APPS for
+ // all users
+ List<UserHandle> profiles =
+ (crossUserRoleUxBugfixEnabled && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP)
+ ? UserUtils.getUserProfiles(context, true) : List.of(user);
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ UserHandle profile = profiles.get(i);
+ UserManager userManager = context.getSystemService(UserManager.class);
+ if (userManager.hasUserRestrictionForUser(
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS, profile)) {
+ return new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)
+ .putExtra(
+ DevicePolicyManager.EXTRA_RESTRICTION,
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS)
+ .putExtra(Intent.EXTRA_USER, profile)
+ .putExtra(IntentCompat.EXTRA_USER_ID, profile.getIdentifier());
+ }
}
}
return null;
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/IntentCompat.java b/PermissionController/role-controller/java/com/android/role/controller/util/IntentCompat.java
new file mode 100644
index 000000000..9771ad4cf
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/IntentCompat.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.role.controller.util;
+
+/** Compat class for {@link android.content.Intent} */
+public final class IntentCompat {
+ /**
+ * An int representing the user ID to be used. Copy of
+ * {@link android.content.Intent#EXTRA_USER_ID}
+ */
+ public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
index 598057b16..00c05b17c 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
@@ -28,6 +28,9 @@ import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
+import java.util.ArrayList;
+import java.util.List;
+
/** Utility class to deal with Android users. */
public final class UserUtils {
@@ -132,4 +135,29 @@ public final class UserUtils {
UserManager userManager = userContext.getSystemService(UserManager.class);
return userManager.getProfileParent(user);
}
+
+ /**
+ * Returns all the enabled user profiles on the device
+ *
+ * @param context the {@link Context}
+ * @param excludePrivate {@code true} to exclude private profiles from returned list of users
+ */
+ @NonNull
+ public static List<UserHandle> getUserProfiles(@NonNull Context context,
+ boolean excludePrivate) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ List<UserHandle> profiles = userManager.getUserProfiles();
+ if (!excludePrivate) {
+ return profiles;
+ }
+ List<UserHandle> filteredProfiles = new ArrayList<>();
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ UserHandle profile = profiles.get(i);
+ if (!isPrivateProfile(profile, context)) {
+ filteredProfiles.add(profile);
+ }
+ }
+ return filteredProfiles;
+ }
}
diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java
index c69afb199..82e9cbbae 100644
--- a/service/java/com/android/permission/util/UserUtils.java
+++ b/service/java/com/android/permission/util/UserUtils.java
@@ -65,7 +65,7 @@ public final class UserUtils {
DevicePolicyManager devicePolicyManager =
context.getSystemService(DevicePolicyManager.class);
if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
- // For profileGroup exclusive roles users on BYOD are free to choose personal o
+ // For profileGroup exclusive roles users on BYOD are free to choose personal or
// work profile app regardless of DISALLOW_DEBUGGING_FEATURES
return;
}
diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
index 724549149..98aa5fbf1 100644
--- a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
+++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt
@@ -25,18 +25,22 @@ import android.content.pm.PackageManager
import android.os.Build
import android.os.Process
import android.os.UserHandle
+import android.os.UserManager.DISALLOW_CONFIG_DEFAULT_APPS
import android.provider.Settings
import android.util.Pair
import androidx.test.filters.SdkSuppress
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.By
import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile
+import com.android.bedstead.enterprise.annotations.EnsureHasUserRestriction
import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile
import com.android.bedstead.enterprise.workProfile
import com.android.bedstead.flags.annotations.RequireFlagsEnabled
import com.android.bedstead.harrier.BedsteadJUnit4
import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.harrier.UserType.INITIAL_USER
+import com.android.bedstead.harrier.UserType.WORK_PROFILE
import com.android.bedstead.multiuser.annotations.EnsureCanAddUser
import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser
import com.android.bedstead.multiuser.annotations.EnsureHasPrivateProfile
@@ -62,6 +66,7 @@ import com.android.compatibility.common.util.SystemUtil
import com.android.compatibility.common.util.SystemUtil.eventually
import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice
import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import java.util.Objects
@@ -69,6 +74,7 @@ import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import org.junit.After
+import org.junit.Assert.assertNull
import org.junit.Assert.assertThrows
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
@@ -1172,8 +1178,10 @@ class RoleManagerMultiUserTest {
}
}
- @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
- com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED)
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
@EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
@EnsureHasWorkProfile
@RequireRunOnWorkProfile
@@ -1575,6 +1583,492 @@ class RoleManagerMultiUserTest {
}
}
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = false)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndOpenDefaultAppWhenBYODHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ // CollapsingToolbar title can't be found by text, so using description instead.
+ waitFindObject(By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL))
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndCannotOpenDefaultAppWhenHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ // CollapsingToolbar title can't be found by text, so using description instead.
+ assertNull(waitFindObjectOrNull(By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL)))
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = WORK_PROFILE)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppListAndCannotOpenDefaultAppWhenHasUserRestrictionOnWorkProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ context.startActivity(
+ Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+ waitFindObject(By.text(PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL)).click()
+ getUiDevice().waitForIdle()
+
+ // CollapsingToolbar title can't be found by text, so using description instead.
+ assertNull(waitFindObjectOrNull(By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL)))
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = false)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppDetailsAndSetDefaultAppWhenBYODHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-target selected first. Request exits early if user and package
+ // already the role holder
+ val initialActiveUser = deviceState.workProfile().userHandle()
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ initialActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ waitFindObject(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppDetailsAndCannotSetDefaultAppWhenHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-target selected first. Request exits early if user and package
+ // already the role holder
+ val initialActiveUser = deviceState.workProfile().userHandle()
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ initialActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ assertNull(
+ waitFindObjectOrNull(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ )
+ } else {
+ assertNull(
+ waitFindObjectOrNull(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(initialActiveUser)
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS)
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = WORK_PROFILE)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun openDefaultAppDetailsAndCannotSetDefaultAppWhenHasUserRestrictionOnWorkProfile() {
+ try {
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-target selected first. Request exits early if user and package
+ // already the role holder
+ val initialActiveUser = deviceState.workProfile().userHandle()
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ initialActiveUser,
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ getUiDevice().waitForIdle()
+
+ val targetActiveUser = users().current().userHandle()
+ val targetAppLabel = "$APP_LABEL@${targetActiveUser.identifier}"
+ if (isWatch) {
+ waitFindObject(By.clickable(true).hasDescendant(By.text(targetAppLabel))).click()
+ } else {
+ waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ .click()
+ }
+
+ if (isWatch) {
+ assertNull(
+ waitFindObjectOrNull(
+ By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel))
+ )
+ )
+ } else {
+ assertNull(
+ waitFindObjectOrNull(
+ By.clickable(true)
+ .hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(targetAppLabel))
+ )
+ )
+ }
+
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(initialActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(initialActiveUser)
+
+ pressBack()
+ pressBack()
+ } finally {
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = false)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleAllowedWhenBYODHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+
+ val targetActiveUser = deviceState.initialUser().userHandle()
+ respondToRoleRequest(true, targetActiveUser)
+
+ // getActiveUserForRole and getRoleHoldersAsUser require INTERACT_ACROSS_USERS_FULL and
+ // MANAGE_ROLE_HOLDERS permissions to validate cross user role active user and role
+ // holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME))
+ .isEqualTo(targetActiveUser)
+ assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser)
+ }
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = INITIAL_USER)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleDeniedWhenHasUserRestrictionOnPrimaryProfile() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ roleRequestNotShown()
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
+ @RequireFlagsEnabled(
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED,
+ com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_UX_BUGFIX_ENABLED,
+ )
+ @EnsureHasUserRestriction(value = DISALLOW_CONFIG_DEFAULT_APPS, onUser = WORK_PROFILE)
+ @EnsureHasWorkProfile(isOrganizationOwned = true)
+ @RequireRunOnPrimaryUser
+ @Test
+ @Throws(java.lang.Exception::class)
+ fun requestRoleDeniedWhenHasUserRestrictionOnWorkProfile() {
+ try {
+ // setDefaultHoldersForTestForAllUsers and setRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ // Set test default role holder. Ensures fallbacks to a default holder
+ setDefaultHoldersForTestForAllUsers()
+ setRoleVisibleForTestForAllUsers()
+
+ // Ensure non-primary selected first. Request exits early if user and package
+ // already the role holder
+ val future = CallbackFuture()
+ roleManager.addRoleHolderAsUser(
+ PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME,
+ APP_PACKAGE_NAME,
+ 0,
+ deviceState.workProfile().userHandle(),
+ context.mainExecutor,
+ future,
+ )
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ requestRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)
+ roleRequestNotShown()
+ } finally {
+ // clearDefaultHoldersForTestForAllUsers and clearRoleVisibleForTestForAllUsers require
+ // INTERACT_ACROSS_USERS_FULL and MANAGE_ROLE_HOLDERS permissions to validate cross user
+ // role active user and role holder states
+ permissions().withPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS).use { _ ->
+ clearDefaultHoldersForTestForAllUsers()
+ clearRoleVisibleForTestForAllUsers()
+ }
+ }
+ }
+
@Throws(java.lang.Exception::class)
private fun installAppForAllUsers() {
SystemUtil.runShellCommandOrThrow("pm install -r --user all $APP_APK_PATH")
@@ -1623,6 +2117,14 @@ class RoleManagerMultiUserTest {
return waitForResult()
}
+ private fun roleRequestNotShown() {
+ val requestRoleItem = waitFindObjectOrNull(By.textStartsWith(APP_LABEL))
+ assertNull(requestRoleItem)
+
+ val result: Pair<Int, Intent?> = waitForResult()
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED)
+ }
+
@Throws(InterruptedException::class)
private fun waitForResult(): Pair<Int, Intent?> {
return activityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS)
@@ -1751,6 +2253,8 @@ class RoleManagerMultiUserTest {
private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong()
private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME =
RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY
+ private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL =
+ "Default test profile group exclusive role app"
private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_SHORT_LABEL =
"Test profile group exclusive role app"
private const val PRIVATE_PROFILE_TYPE_NAME = "android.os.usertype.profile.PRIVATE"