Loading core/java/android/content/pm/multiuser.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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 } } core/java/android/os/UserManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading services/core/java/com/android/server/pm/UserManagerService.java +31 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -6261,9 +6262,11 @@ 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) { Loading Loading @@ -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) { Loading Loading @@ -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; } } Loading services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -537,6 +537,79 @@ public final class UserManagerTest { removeUser(testUser.id); } @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() { Loading Loading
core/java/android/content/pm/multiuser.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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 } }
core/java/android/os/UserManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading
services/core/java/com/android/server/pm/UserManagerService.java +31 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -6261,9 +6262,11 @@ 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) { Loading Loading @@ -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) { Loading Loading @@ -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; } } Loading
services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -537,6 +537,79 @@ public final class UserManagerTest { removeUser(testUser.id); } @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() { Loading