diff options
8 files changed, 246 insertions, 26 deletions
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 787e19e55..23f5d8344 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -1871,8 +1871,10 @@ --> <role name="android.app.role.RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY" + behavior="ReservedForTestingProfileGroupExclusivityRoleBehavior" exclusive="true" exclusivity="profileGroup" + fallBackToDefaultHolder="true" featureFlag="com.android.permission.flags.Flags.crossUserRoleEnabled" showNone="true" visible="false"/> diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java new file mode 100644 index 000000000..71f988279 --- /dev/null +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 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.behavior; + +import android.content.Context; +import android.os.UserHandle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.role.controller.model.Role; +import com.android.role.controller.model.RoleBehavior; + +import java.util.Arrays; +import java.util.List; + +public class ReservedForTestingProfileGroupExclusivityRoleBehavior implements RoleBehavior { + // TODO(b/381315745): Update to use API for setting and getting test role default holders. + // This role doesn't grant any privileges, so this should be ok. + private static final List<String> DEFAULT_HOLDERS = + Arrays.asList("android.app.rolemultiuser.cts.app"); + + @Nullable + @Override + public List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user, + @NonNull Context context) { + return DEFAULT_HOLDERS; + } +} 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 48bc34b8f..c551c37dc 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 @@ -520,6 +520,12 @@ public class Role { @NonNull public List<String> getDefaultHoldersAsUser(@NonNull UserHandle user, @NonNull Context context) { + // Do not allow default role holder for non-active user if the role is exclusive to profile + // group + if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) { + return Collections.emptyList(); + } + if (mBehavior != null) { List<String> defaultHolders = mBehavior.getDefaultHoldersAsUser(this, user, context); if (defaultHolders != null) { @@ -631,6 +637,10 @@ public class Role { if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, user, context)) { return null; } + // Do not fall back for non-active user if the role is exclusive to profile group + if (isNonActiveUserForProfileGroupExclusiveRole(user, context)) { + return null; + } if (mFallBackToDefaultHolder) { return CollectionUtils.firstOrNull(getDefaultHoldersAsUser(user, context)); } @@ -640,6 +650,17 @@ public class Role { return null; } + private boolean isNonActiveUserForProfileGroupExclusiveRole(@NonNull UserHandle user, + @NonNull Context context) { + if (RoleFlags.isProfileGroupExclusivityAvailable() + && getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) { + Context userContext = UserUtils.getUserContext(context, user); + RoleManager userRoleManager = userContext.getSystemService(RoleManager.class); + return !Objects.equals(userRoleManager.getActiveUserForRole(mName), user); + } + return false; + } + /** * Check whether this role is allowed to bypass qualification, if enabled globally. * diff --git a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java index a5ac5700e..d00fd47af 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java +++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java @@ -35,6 +35,7 @@ import com.android.role.controller.model.Roles; import com.android.role.controller.util.CollectionUtils; import com.android.role.controller.util.LegacyRoleFallbackEnabledUtils; import com.android.role.controller.util.PackageUtils; +import com.android.role.controller.util.RoleFlags; import com.android.role.controller.util.UserUtils; import java.util.ArrayList; @@ -132,6 +133,19 @@ public class RoleControllerServiceImpl extends RoleControllerService { String roleName = role.getName(); + if (RoleFlags.isProfileGroupExclusivityAvailable() + && role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) { + if (mUserRoleManager.getActiveUserForRole(roleName) == null) { + UserHandle profileParent = UserUtils.getProfileParentOrSelf(mUser, mContext); + if (Objects.equals(mUser, profileParent)) { + Log.i(LOG_TAG, "No active user for role: " + roleName + ", setting " + + "active user to user: " + mUser.getIdentifier()); + sSetActiveUserForRoleMethod.setActiveUserForRole(roleName, + mUser.getIdentifier(), 0); + } + } + } + // For each of the current holders, check if it is still qualified, redo grant if so, or // remove it otherwise. List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName); diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java index f8a8502cd..2c5a247b6 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java +++ b/PermissionController/role-controller/java/com/android/role/controller/util/RoleFlags.java @@ -20,7 +20,7 @@ import android.os.Build; import androidx.annotation.ChecksSdkIntAtLeast; -import com.android.modules.utils.build.SdkLevel; +import java.util.Objects; /** Util class for getting shared feature flag check logic. */ public final class RoleFlags { @@ -30,9 +30,16 @@ public final class RoleFlags { * Returns whether profile group exclusive roles are available. Profile exclusive roles are * available on B+ */ - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.VANILLA_ICE_CREAM) + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.BAKLAVA) public static boolean isProfileGroupExclusivityAvailable() { // TODO(b/372743073): change to isAtLeastB once available - return SdkLevel.isAtLeastV() && com.android.permission.flags.Flags.crossUserRoleEnabled(); + return isAtLeastB() && com.android.permission.flags.Flags.crossUserRoleEnabled(); + } + + // TODO(b/372743073): remove once SdkLevel.isAtLeastB available + @ChecksSdkIntAtLeast(api = 36 /* BUILD_VERSION_CODES.Baklava */) + public static boolean isAtLeastB() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA + || Objects.equals(Build.VERSION.CODENAME, "Baklava"); } } diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java index 9f89140d7..92ea5d98c 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java +++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java @@ -1352,6 +1352,7 @@ public class RoleManagerTest { } @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test public void cannotGetActiveUserForRoleWithoutPermission() throws Exception { assertThrows(SecurityException.class, ()-> @@ -1359,6 +1360,7 @@ public class RoleManagerTest { } @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test public void cannotGetActiveUserForNonProfileGroupExclusiveRole() throws Exception { runWithShellPermissionIdentity(() -> @@ -1368,6 +1370,7 @@ public class RoleManagerTest { } @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test public void cannotSetActiveUserForRoleWithoutPermission() throws Exception { assertThrows(SecurityException.class, ()-> @@ -1376,6 +1379,7 @@ public class RoleManagerTest { } @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test public void cannotSetActiveUserForNonProfileGroupExclusiveRole() throws Exception { runWithShellPermissionIdentity(() -> @@ -1386,6 +1390,7 @@ public class RoleManagerTest { } @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test public void setAndGetActiveUserForRole() throws Exception { runWithShellPermissionIdentity(() -> { diff --git a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt index f615f0f4b..c9dc97b8f 100644 --- a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt +++ b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt @@ -163,6 +163,7 @@ class RoleShellCommandTest { } @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test fun setActiveUserForProfileGroupExclusiveRoleAsUser() { val activeUser = userId @@ -173,12 +174,14 @@ class RoleShellCommandTest { } @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test fun setActiveUserForNonProfileGroupExclusiveRoleThenFails() { assertThrows(AssertionError::class.java) { setActiveUserForRole(ROLE_NAME, userId) } } @RequiresFlagsEnabled(Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test fun getActiveUserForNonProfileGroupExclusiveRoleThenFails() { assertThrows(AssertionError::class.java) { getActiveUserForRole(ROLE_NAME) } 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 6c9cdfcb8..e4f9ce55c 100644 --- a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt +++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt @@ -19,6 +19,7 @@ import android.app.role.RoleManager import android.content.Context import android.os.Build import android.os.Process +import android.os.UserHandle import androidx.test.filters.SdkSuppress import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile import com.android.bedstead.enterprise.annotations.RequireRunOnWorkProfile @@ -43,7 +44,10 @@ import com.android.bedstead.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS import com.android.bedstead.permissions.annotations.EnsureDoesNotHavePermission import com.android.bedstead.permissions.annotations.EnsureHasPermission import com.android.compatibility.common.util.SystemUtil +import com.android.compatibility.common.util.SystemUtil.eventually import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import java.util.Objects import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit import java.util.function.Consumer @@ -56,7 +60,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @RunWith(BedsteadJUnit4::class) class RoleManagerMultiUserTest { @Before @@ -189,6 +193,45 @@ class RoleManagerMultiUserTest { @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @EnsureHasAdditionalUser(installInstrumentedApp = OptionalBoolean.TRUE) + @EnsureHasSecondaryUser + @RequireRunNotOnSecondaryUser + @Test + @Throws(java.lang.Exception::class) + fun ensureRoleHasActiveUser() { + val primaryUser = deviceState.initialUser().userHandle() + val primaryUserRoleManager = getRoleManagerForUser(primaryUser) + val secondaryUser = deviceState.secondaryUser().userHandle() + val secondaryUserRoleManager = getRoleManagerForUser(secondaryUser) + + assertWithMessage( + "Expected active user in profile group for user ${primaryUser.identifier}" + ) + .that(primaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isNotNull() + assertWithMessage( + "Expected active user in profile group for user ${secondaryUser.identifier}" + ) + .that( + secondaryUserRoleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isNotNull() + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile + @Test + @Throws(java.lang.Exception::class) + fun ensureOnlyActiveUserIsRoleHolder() { + val activeUser = roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)!! + // Test app install might take a moment + eventually { assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(activeUser) } + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) @EnsureDoesNotHavePermission(MANAGE_DEFAULT_APPLICATIONS) @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) @Test @@ -228,12 +271,41 @@ class RoleManagerMultiUserTest { @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) @Test @Throws(Exception::class) + fun setAndGetActiveUserForRoleSetCurrentUserEnsureRoleNotHeldByInactiveUser() { + assumeFalse( + "setActiveUser not supported for private profile", + users().current().type().name() == PRIVATE_PROFILE_TYPE_NAME, + ) + // initialUser needs to be not the targetUser + val targetActiveUser = users().current().userHandle() + val initialUser = + if (Objects.equals(targetActiveUser, deviceState.initialUser())) { + deviceState.workProfile().userHandle() + } else { + deviceState.initialUser().userHandle() + } + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) + + roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) + assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) + .isEqualTo(targetActiveUser) + // We can assume targetActiveUser is role holder since fallback is enabled + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) + } + + @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) + @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) + @EnsureHasWorkProfile(installInstrumentedApp = OptionalBoolean.TRUE) + @Test + @Throws(Exception::class) fun setAndGetActiveUserForRoleSetWorkProfile() { val targetActiveUser = deviceState.workProfile().userHandle() roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser, 0) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) .isEqualTo(targetActiveUser) + // We can assume targetActiveUser is role holder since fallback is enabled + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) } @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @@ -285,10 +357,9 @@ class RoleManagerMultiUserTest { @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_ROLE_HOLDERS) @EnsureHasWorkProfile - @RequireRunOnPrimaryUser @Test @Throws(java.lang.Exception::class) - fun addRoleHolderAsUserSetsPrimaryUserAsActive() { + fun addRoleHolderAsUserSetsCurrentUserAsActive() { // Set other user as active val initialUser = deviceState.workProfile().userHandle() roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) @@ -306,14 +377,9 @@ class RoleManagerMultiUserTest { future, ) assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() - assertThat( - roleManager - .getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser) - .first() - ) - .isEqualTo(APP_PACKAGE_NAME) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) } @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @@ -330,6 +396,8 @@ class RoleManagerMultiUserTest { .isEqualTo(initialUser) val targetActiveUser = deviceState.workProfile().userHandle() + + assertThat(targetActiveUser).isNotEqualTo(initialUser) val future = CallbackFuture() roleManager.addRoleHolderAsUser( PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, @@ -340,14 +408,9 @@ class RoleManagerMultiUserTest { future, ) assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() - assertThat( - roleManager - .getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, targetActiveUser) - .first() - ) - .isEqualTo(APP_PACKAGE_NAME) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser(targetActiveUser) } @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @@ -391,16 +454,16 @@ class RoleManagerMultiUserTest { @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @EnsureHasPermission(INTERACT_ACROSS_USERS_FULL, MANAGE_DEFAULT_APPLICATIONS) @EnsureHasWorkProfile - @RequireRunOnPrimaryUser @Test @Throws(java.lang.Exception::class) - fun setDefaultApplicationSetsPrimaryUserAsActive() { + fun setDefaultApplicationSetsCurrentUserAsActive() { // Set other user as active val initialUser = deviceState.workProfile().userHandle() roleManager.setActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, initialUser, 0) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) .isEqualTo(initialUser) + val targetActiveUser = users().current().userHandle() val future = CallbackFuture() roleManager.setDefaultApplication( PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, @@ -410,10 +473,9 @@ class RoleManagerMultiUserTest { future, ) assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() - assertThat(roleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) - .isEqualTo(APP_PACKAGE_NAME) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) - .isEqualTo(users().current().userHandle()) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) } @RequireFlagsEnabled(com.android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_ENABLED) @@ -429,6 +491,8 @@ class RoleManagerMultiUserTest { assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) .isEqualTo(initialUser) + val targetActiveUser = deviceState.workProfile().userHandle() + assertThat(targetActiveUser).isNotEqualTo(initialUser) val future = CallbackFuture() roleManager.setDefaultApplication( PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, @@ -438,10 +502,9 @@ class RoleManagerMultiUserTest { future, ) assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue() - assertThat(roleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) - .isEqualTo(APP_PACKAGE_NAME) assertThat(roleManager.getActiveUserForRole(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME)) - .isEqualTo(deviceState.workProfile().userHandle()) + .isEqualTo(targetActiveUser) + assertExpectedProfileHasRoleUsingGetDefaultApplication(targetActiveUser) } @Throws(java.lang.Exception::class) @@ -453,6 +516,68 @@ class RoleManagerMultiUserTest { SystemUtil.runShellCommand("pm uninstall $APP_PACKAGE_NAME") } + private fun assertExpectedProfileHasRoleUsingGetRoleHoldersAsUser( + expectedActiveUser: UserHandle + ) { + users().profileGroup().forEach { userReference -> + val user = userReference.userHandle() + if (Objects.equals(user, expectedActiveUser)) { + val roleHolders = + roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + assertWithMessage( + "Expected user ${user.identifier} to have a role holder for" + + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that(roleHolders) + .isNotEmpty() + assertWithMessage( + "Expected user ${user.identifier} to have $APP_PACKAGE_NAME as role " + + "holder for $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that(roleHolders.first()) + .isEqualTo(APP_PACKAGE_NAME) + } else { + // Verify the non-active user does not hold the role + assertWithMessage( + "Expected user ${user.identifier} to not have a role holder for" + + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" + ) + .that( + roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + ) + .isEmpty() + } + } + } + + private fun assertExpectedProfileHasRoleUsingGetDefaultApplication( + expectedActiveUser: UserHandle + ) { + users().profileGroup().forEach { userReference -> + val user = userReference.userHandle() + val userRoleManager = getRoleManagerForUser(user) + if (Objects.equals(user, expectedActiveUser)) { + assertWithMessage("Expected default application for user ${user.identifier}") + .that( + userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isEqualTo(APP_PACKAGE_NAME) + } else { + // Verify the non-active user does not hold the role + assertWithMessage("Expected no default application for user ${user.identifier}") + .that( + userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) + ) + .isNull() + } + } + } + + private fun getRoleManagerForUser(user: UserHandle): RoleManager { + val userContext = context.createContextAsUser(user, 0) + return userContext.getSystemService(RoleManager::class.java) + } + class CallbackFuture : CompletableFuture<Boolean?>(), Consumer<Boolean?> { override fun accept(successful: Boolean?) { complete(successful) |