Loading core/java/android/os/UserManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -4824,6 +4824,7 @@ public class UserManager { * <p>Note that this does not alter the user's pre-existing user restrictions. * * @param userId the id of the user to become admin * @throws SecurityException if changing ADMIN status of the user is not allowed * @hide */ @RequiresPermission(allOf = { Loading @@ -4844,6 +4845,7 @@ public class UserManager { * <p>Note that this does not alter the user's pre-existing user restrictions. * * @param userId the id of the user to revoke admin rights from * @throws SecurityException if changing ADMIN status of the user is not allowed * @hide */ @RequiresPermission(allOf = { Loading services/core/java/com/android/server/pm/UserManagerService.java +35 −0 Original line number Diff line number Diff line Loading @@ -2105,6 +2105,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); } mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN); UserData user; synchronized (mPackagesLock) { Loading Loading @@ -2133,6 +2137,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void revokeUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); } mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN); UserData user; synchronized (mPackagesLock) { Loading Loading @@ -4065,6 +4073,26 @@ public class UserManagerService extends IUserManager.Stub { } } /** * Checks if changing the admin status of a target user is restricted * due to the DISALLOW_GRANT_ADMIN restriction. If either the calling * user or the target user has this restriction, a SecurityException * is thrown. * * @param targetUser The user ID of the user whose admin status is being * considered for change. * @throws SecurityException if the admin status change is restricted due * to the DISALLOW_GRANT_ADMIN restriction. */ private void checkAdminStatusChangeAllowed(int targetUser) { if (hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId()) || hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, targetUser)) { throw new SecurityException( "Admin status change is restricted. The DISALLOW_GRANT_ADMIN " + "restriction is applied either on the current or the target user."); } } @GuardedBy({"mPackagesLock"}) private void writeBitmapLP(UserInfo info, Bitmap bitmap) { try { Loading Loading @@ -5443,6 +5471,13 @@ public class UserManagerService extends IUserManager.Stub { enforceUserRestriction(restriction, UserHandle.getCallingUserId(), "Cannot add user"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { if ((flags & UserInfo.FLAG_ADMIN) != 0) { enforceUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId(), "Cannot create ADMIN user"); } } return createUserInternalUnchecked(name, userType, flags, parentId, /* preCreate= */ false, disallowedPackages, /* token= */ null); } Loading services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +80 −0 Original line number Diff line number Diff line Loading @@ -121,6 +121,9 @@ public final class UserManagerTest { // Making a copy of mUsersToRemove to avoid ConcurrentModificationException mUsersToRemove.stream().toList().forEach(this::removeUser); mUserRemovalWaiter.close(); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } private void removeExistingUsers() { Loading Loading @@ -933,6 +936,35 @@ public final class UserManagerTest { assertThat(userInfo.isAdmin()).isTrue(); } @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testSetUserAdminThrowsSecurityException() throws Exception { UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0); assertThat(targetUser.isAdmin()).isFalse(); try { // 1. Target User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, targetUser.getUserHandle()); assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id)); // 2. Current User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, mContext.getUser()); assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id)); } finally { // Ensure restriction is removed even if test fails mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } } @MediumTest @Test public void testRevokeUserAdmin() throws Exception { Loading @@ -957,6 +989,37 @@ public final class UserManagerTest { assertThat(userInfo.isAdmin()).isFalse(); } @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testRevokeUserAdminThrowsSecurityException() throws Exception { UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0); assertThat(targetUser.isAdmin()).isFalse(); try { // 1. Target User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, targetUser.getUserHandle()); assertThrows(SecurityException.class, () -> mUserManager .revokeUserAdmin(targetUser.id)); // 2. Current User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, mContext.getUser()); assertThrows(SecurityException.class, () -> mUserManager .revokeUserAdmin(targetUser.id)); } finally { // Ensure restriction is removed even if test fails mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } } @MediumTest @Test public void testGetProfileParent() throws Exception { Loading Loading @@ -1184,6 +1247,23 @@ public final class UserManagerTest { } } // Make sure createUser for ADMIN would fail if we have DISALLOW_GRANT_ADMIN. @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testCreateAdminUser_disallowGrantAdmin() throws Exception { final int creatorId = ActivityManager.getCurrentUser(); final UserHandle creatorHandle = asHandle(creatorId); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, creatorHandle); try { UserInfo createdInfo = createUser("SecondaryUser", /*flags=*/ UserInfo.FLAG_ADMIN); assertThat(createdInfo).isNull(); } finally { mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, creatorHandle); } } // Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE. @MediumTest @Test Loading Loading
core/java/android/os/UserManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -4824,6 +4824,7 @@ public class UserManager { * <p>Note that this does not alter the user's pre-existing user restrictions. * * @param userId the id of the user to become admin * @throws SecurityException if changing ADMIN status of the user is not allowed * @hide */ @RequiresPermission(allOf = { Loading @@ -4844,6 +4845,7 @@ public class UserManager { * <p>Note that this does not alter the user's pre-existing user restrictions. * * @param userId the id of the user to revoke admin rights from * @throws SecurityException if changing ADMIN status of the user is not allowed * @hide */ @RequiresPermission(allOf = { Loading
services/core/java/com/android/server/pm/UserManagerService.java +35 −0 Original line number Diff line number Diff line Loading @@ -2105,6 +2105,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); } mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN); UserData user; synchronized (mPackagesLock) { Loading Loading @@ -2133,6 +2137,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void revokeUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); } mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN); UserData user; synchronized (mPackagesLock) { Loading Loading @@ -4065,6 +4073,26 @@ public class UserManagerService extends IUserManager.Stub { } } /** * Checks if changing the admin status of a target user is restricted * due to the DISALLOW_GRANT_ADMIN restriction. If either the calling * user or the target user has this restriction, a SecurityException * is thrown. * * @param targetUser The user ID of the user whose admin status is being * considered for change. * @throws SecurityException if the admin status change is restricted due * to the DISALLOW_GRANT_ADMIN restriction. */ private void checkAdminStatusChangeAllowed(int targetUser) { if (hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId()) || hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, targetUser)) { throw new SecurityException( "Admin status change is restricted. The DISALLOW_GRANT_ADMIN " + "restriction is applied either on the current or the target user."); } } @GuardedBy({"mPackagesLock"}) private void writeBitmapLP(UserInfo info, Bitmap bitmap) { try { Loading Loading @@ -5443,6 +5471,13 @@ public class UserManagerService extends IUserManager.Stub { enforceUserRestriction(restriction, UserHandle.getCallingUserId(), "Cannot add user"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { if ((flags & UserInfo.FLAG_ADMIN) != 0) { enforceUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId(), "Cannot create ADMIN user"); } } return createUserInternalUnchecked(name, userType, flags, parentId, /* preCreate= */ false, disallowedPackages, /* token= */ null); } Loading
services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +80 −0 Original line number Diff line number Diff line Loading @@ -121,6 +121,9 @@ public final class UserManagerTest { // Making a copy of mUsersToRemove to avoid ConcurrentModificationException mUsersToRemove.stream().toList().forEach(this::removeUser); mUserRemovalWaiter.close(); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } private void removeExistingUsers() { Loading Loading @@ -933,6 +936,35 @@ public final class UserManagerTest { assertThat(userInfo.isAdmin()).isTrue(); } @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testSetUserAdminThrowsSecurityException() throws Exception { UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0); assertThat(targetUser.isAdmin()).isFalse(); try { // 1. Target User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, targetUser.getUserHandle()); assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id)); // 2. Current User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, mContext.getUser()); assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id)); } finally { // Ensure restriction is removed even if test fails mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } } @MediumTest @Test public void testRevokeUserAdmin() throws Exception { Loading @@ -957,6 +989,37 @@ public final class UserManagerTest { assertThat(userInfo.isAdmin()).isFalse(); } @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testRevokeUserAdminThrowsSecurityException() throws Exception { UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0); assertThat(targetUser.isAdmin()).isFalse(); try { // 1. Target User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, targetUser.getUserHandle()); assertThrows(SecurityException.class, () -> mUserManager .revokeUserAdmin(targetUser.id)); // 2. Current User Restriction mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, mContext.getUser()); assertThrows(SecurityException.class, () -> mUserManager .revokeUserAdmin(targetUser.id)); } finally { // Ensure restriction is removed even if test fails mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, targetUser.getUserHandle()); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, mContext.getUser()); } } @MediumTest @Test public void testGetProfileParent() throws Exception { Loading Loading @@ -1184,6 +1247,23 @@ public final class UserManagerTest { } } // Make sure createUser for ADMIN would fail if we have DISALLOW_GRANT_ADMIN. @MediumTest @Test @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY) public void testCreateAdminUser_disallowGrantAdmin() throws Exception { final int creatorId = ActivityManager.getCurrentUser(); final UserHandle creatorHandle = asHandle(creatorId); mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, creatorHandle); try { UserInfo createdInfo = createUser("SecondaryUser", /*flags=*/ UserInfo.FLAG_ADMIN); assertThat(createdInfo).isNull(); } finally { mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, creatorHandle); } } // Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE. @MediumTest @Test Loading