diff options
4 files changed, 119 insertions, 15 deletions
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index fa3bc9e16a6b..35f9cff1aedb 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -533,3 +533,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "ignore_restrictions_when_deleting_private_profile" + namespace: "multiuser" + description: "Ignore any user restrictions when deleting private profiles." + bug: "350953833" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index bd3da0d56cbd..acf4a2f3ec71 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -772,9 +772,10 @@ public class UserManager { public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials"; /** - * When set on the admin user this specifies if the user can remove users. + * When set on the admin user this specifies if the user can remove secondary users. Managed + * profiles and private profiles can still be removed even if this is set on the admin user. * When set on a non-admin secondary user, this specifies if the user can remove itself. - * This restriction has no effect on managed profiles. + * This restriction has no effect when set on managed profiles. * The default value is <code>false</code>. * * <p>Holders of the permission @@ -793,7 +794,8 @@ public class UserManager { * Specifies if managed profiles of this user can be removed, other than by its profile owner. * The default value is <code>false</code>. * <p> - * This restriction has no effect on managed profiles. + * This restriction has no effect on managed profiles, and this restriction does not block the + * removal of private profiles of this user. * * <p>Key for user restrictions. * <p>Type: Boolean diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index daf413bb30b3..6ac8b221b21f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -195,6 +195,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @@ -6261,14 +6262,16 @@ public class UserManagerService extends IUserManager.Stub { Slog.i(LOG_TAG, "removeUser u" + userId); checkCreateUsersPermission("Only the system can remove users"); - final String restriction = getUserRemovalRestriction(userId); - if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) { - Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled."); + final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId); + if (!restrictionOptional.isEmpty() + && getUserRestrictions(UserHandle.getCallingUserId()) + .getBoolean(restrictionOptional.get(), false)) { + Slog.w(LOG_TAG, "Cannot remove user. " + restrictionOptional.get() + " is enabled."); return false; } if (mCurrentBootPhase < SystemService.PHASE_ACTIVITY_MANAGER_READY) { Slog.w(LOG_TAG, "Cannot remove user, removeUser is called too early during boot. " - + "ActivityManager is not ready yet."); + + "ActivityManager is not ready yet."); return false; } return removeUserWithProfilesUnchecked(userId); @@ -6335,18 +6338,30 @@ public class UserManagerService extends IUserManager.Stub { } /** - * Returns the string name of the restriction to check for user removal. The restriction name - * varies depending on whether the user is a managed profile. + * Returns an optional string name of the restriction to check for user removal. The restriction + * name varies depending on whether the user is a managed profile. + * + * <p>If the flag android.multiuser.ignore_restrictions_when_deleting_private_profile is enabled + * and the user is a private profile (i.e. has no removal restrictions) the method will return + * {@code Optional.empty()}. */ - private String getUserRemovalRestriction(@UserIdInt int userId) { + private Optional<String> getUserRemovalRestrictionOptional(@UserIdInt int userId) { + final boolean isPrivateProfile; final boolean isManagedProfile; final UserInfo userInfo; synchronized (mUsersLock) { userInfo = getUserInfoLU(userId); } + isPrivateProfile = userInfo != null && userInfo.isPrivateProfile(); isManagedProfile = userInfo != null && userInfo.isManagedProfile(); - return isManagedProfile - ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; + if (android.multiuser.Flags.ignoreRestrictionsWhenDeletingPrivateProfile() + && isPrivateProfile) { + return Optional.empty(); + } + return Optional.of( + isManagedProfile + ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE + : UserManager.DISALLOW_REMOVE_USER); } private boolean removeUserUnchecked(@UserIdInt int userId) { @@ -6455,9 +6470,13 @@ public class UserManagerService extends IUserManager.Stub { checkCreateUsersPermission("Only the system can remove users"); if (!overrideDevicePolicy) { - final String restriction = getUserRemovalRestriction(userId); - if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) { - Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled."); + final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId); + if (!restrictionOptional.isEmpty() + && getUserRestrictions(UserHandle.getCallingUserId()) + .getBoolean(restrictionOptional.get(), false)) { + Slog.w( + LOG_TAG, + "Cannot remove user. " + restrictionOptional.get() + " is enabled."); return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION; } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index e652df5cdf1a..f9946604ad5d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -539,6 +539,79 @@ public final class UserManagerTest { @MediumTest @Test + public void testRemoveUser_shouldRemovePrivateUser() { + UserInfo privateProfileUser = + createProfileForUser( + "Private profile", + UserManager.USER_TYPE_PROFILE_PRIVATE, + mUserManager.getMainUser().getIdentifier()); + assertThat(privateProfileUser).isNotNull(); + assertThat(hasUser(privateProfileUser.id)).isTrue(); + + removeUser(privateProfileUser.id); + + assertThat(hasUser(privateProfileUser.id)).isFalse(); + } + + @MediumTest + @Test + @RequiresFlagsEnabled( + android.multiuser.Flags.FLAG_IGNORE_RESTRICTIONS_WHEN_DELETING_PRIVATE_PROFILE) + public void testRemoveUser_shouldRemovePrivateUser_withDisallowRemoveUserRestriction() { + UserHandle mainUser = mUserManager.getMainUser(); + mUserManager.setUserRestriction( + UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser); + try { + UserInfo privateProfileUser = + createProfileForUser( + "Private profile", + UserManager.USER_TYPE_PROFILE_PRIVATE, + mainUser.getIdentifier()); + assertThat(privateProfileUser).isNotNull(); + assertThat(hasUser(privateProfileUser.id)).isTrue(); + removeUser(privateProfileUser.id); + + assertThat(hasUser(privateProfileUser.id)).isFalse(); + } finally { + mUserManager.setUserRestriction( + UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser); + } + } + + @MediumTest + @Test + public void testRemoveUser_withDisallowRemoveUserRestrictionAndMultipleUsersPresent() { + UserInfo privateProfileUser = + createProfileForUser( + "Private profile", + UserManager.USER_TYPE_PROFILE_PRIVATE, + mUserManager.getMainUser().getIdentifier()); + assertThat(privateProfileUser).isNotNull(); + assertThat(hasUser(privateProfileUser.id)).isTrue(); + UserInfo testUser = createUser("TestUser", /* flags= */ 0); + assertThat(testUser).isNotNull(); + assertThat(hasUser(testUser.id)).isTrue(); + UserHandle mainUser = mUserManager.getMainUser(); + mUserManager.setUserRestriction( + UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser); + try { + assertThat( + mUserManager.removeUserWhenPossible( + testUser.getUserHandle(), /* overrideDevicePolicy= */ false)) + .isEqualTo(UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION); + + // Non private profile users should be prevented from being removed. + assertThat(mUserManager.removeUser(testUser.id)).isEqualTo(false); + + assertThat(hasUser(testUser.id)).isTrue(); + } finally { + mUserManager.setUserRestriction( + UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser); + } + } + + @MediumTest + @Test public void testRemoveUserShouldNotRemoveTargetUser_DuringUserSwitch() { final int startUser = ActivityManager.getCurrentUser(); final UserInfo testUser = createUser("TestUser", /* flags= */ 0); |