Loading services/core/java/com/android/server/pm/UserManagerService.java +30 −8 Original line number Diff line number Diff line Loading @@ -2390,6 +2390,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { setUserAdminInternal(userId); } // NOTE: split in 2 methods because cmd user needs a boolean but the AIDL one doesn't boolean setUserAdminInternal(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); Loading @@ -2405,30 +2410,41 @@ public class UserManagerService extends IUserManager.Stub { // Exit if no user found with that id, mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, currentUserId, userId, /* userType */ "", /* userFlags */ -1); return; Slogf.w(LOG_TAG, "setUserAdmin(%d) failed: user not found", userId); return false; } else if (user.info.isAdmin()) { // Exit if the user is already an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_USER_ALREADY_AN_ADMIN); return; Slogf.w(LOG_TAG, "setUserAdmin(%d): not changed, already admin", userId); return true; } else if (user.info.isProfile() || user.info.isGuest() || user.info.isRestricted()) { // Profiles, guest users or restricted profiles cannot become an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_INVALID_USER_TYPE); return; Slogf.w(LOG_TAG, "setUserAdmin(%d) failed: profile, guest, and restricted users" + " cannot be admins (user: %s) ", userId, user.info.toFullString()); return false; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); } } Slogf.i(LOG_TAG, "setUserAdmin(%d): succeeded", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_UNSPECIFIED); return true; } @Override public void revokeUserAdmin(@UserIdInt int userId) { revokeUserAdminInternal(userId); } // NOTE: split in 2 methods because cmd user needs a boolean but the AIDL one doesn't boolean revokeUserAdminInternal(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); Loading @@ -2444,23 +2460,27 @@ public class UserManagerService extends IUserManager.Stub { // Exit if no user found with that id mUserJourneyLogger.logNullUserJourneyError( USER_JOURNEY_REVOKE_ADMIN, currentUserId, userId, "", -1); return; Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: user not found", userId); return false; } else if (!user.info.isAdmin()) { // Exit if user is not an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_NOT_AN_ADMIN); return; Slogf.w(LOG_TAG, "revokeUserAdmin(%d): not changed, already not admin", userId); return true; } else if ((user.info.flags & UserInfo.FLAG_SYSTEM) != 0) { // System user cannot lose its admin status. Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: system user", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_INVALID_USER_TYPE); return; return false; } else if (isNonRemovableLastAdminUserLU(user.info)) { // This is the last admin user and this device requires that it not lose its // admin status. Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: user is last admin", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_LAST_ADMIN); return; return false; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); Loading @@ -2468,6 +2488,8 @@ public class UserManagerService extends IUserManager.Stub { } mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_UNSPECIFIED); Slogf.i(LOG_TAG, "revokeUserAdmin(%d): succeeded", userId); return true; } /** Loading services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java +24 −18 Original line number Diff line number Diff line Loading @@ -126,11 +126,11 @@ public class UserManagerServiceShellCommand extends ShellCommand { pw.println(" get-main-user "); pw.println(" Displays main user id or message if there is no main user"); pw.println(); pw.println(" set-user-admin <USER_ID>"); pw.println(" Sets the given user as an admin"); pw.println(" grant-admin <USER_ID>"); pw.println(" Grants admin privileges to the given user (requires adb root)"); pw.println(); pw.println(" revoke-user-admin <USER_ID>"); pw.println(" Revokes the given user as an admin"); pw.println(" revoke-admin <USER_ID>"); pw.println(" Revokes admin privileges from the given user (requires adb root)"); pw.println(); } Loading Loading @@ -162,10 +162,10 @@ public class UserManagerServiceShellCommand extends ShellCommand { return runCanSwitchToHeadlessSystemUser(); case "is-main-user-permanent-admin": return runIsMainUserPermanentAdmin(); case "set-user-admin": return runSetUserAdmin(); case "revoke-user-admin": return runRevokeUserAdmin(); case "grant-admin": return runGrantAdmin(); case "revoke-admin": return runRevokeAdmin(); default: return handleDefaultCommands(cmd); } Loading Loading @@ -570,15 +570,15 @@ public class UserManagerServiceShellCommand extends ShellCommand { return 0; } private int runSetUserAdmin() throws RemoteException { return setOrRevokeAdmin(/* set= */ true); private int runGrantAdmin() throws RemoteException { return grantOrRevokeAdmin(/* grant= */ true); } private int runRevokeUserAdmin() throws RemoteException { return setOrRevokeAdmin(/* set= */ false); private int runRevokeAdmin() throws RemoteException { return grantOrRevokeAdmin(/* grant= */ false); } private int setOrRevokeAdmin(boolean set) { private int grantOrRevokeAdmin(boolean grant) { if (!confirmBuildIsDebuggable() || !confirmIsCalledByRoot()) { return -1; } Loading @@ -586,15 +586,21 @@ public class UserManagerServiceShellCommand extends ShellCommand { if (userId == UserHandle.USER_NULL) { return -1; } if (set) { boolean success; if (grant) { Slogf.i(LOG_TAG, "Calling setUserAdmin(%d)", userId); mService.setUserAdmin(userId); success = mService.setUserAdminInternal(userId); } else { Slogf.i(LOG_TAG, "Calling revokeUserAdmin(%d)", userId); mService.revokeUserAdmin(userId); success = mService.revokeUserAdminInternal(userId); } if (success) { getOutPrintWriter().println("Success"); return 0; } else { getOutPrintWriter().println("Failed"); return -1; } } /** Loading services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +39 −14 Original line number Diff line number Diff line Loading @@ -1789,9 +1789,9 @@ public final class UserManagerServiceMockedTest { public void testSetUserAdmin() { addSecondaryUser(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isTrue(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isTrue(); } @Test Loading @@ -1815,9 +1815,9 @@ public final class UserManagerServiceMockedTest { public void testSetUserAdminFailsForGuest() { addGuestUser(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isFalse(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test Loading @@ -1825,34 +1825,34 @@ public final class UserManagerServiceMockedTest { addSecondaryUser(PARENT_USER_ID); addProfile(PROFILE_USER_ID, PARENT_USER_ID, USER_TYPE_PROFILE_MANAGED); mUms.setUserAdmin(PROFILE_USER_ID); expect.that(mUms.setUserAdminInternal(PROFILE_USER_ID)).isFalse(); assertThat(mUsers.get(PROFILE_USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(PROFILE_USER_ID).info.isAdmin()).isFalse(); } @Test public void testSetUserAdminFailsForRestrictedProfile() { addRestrictedProfile(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isFalse(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test public void testRevokeUserAdmin() { addAdminUser(USER_ID); mUms.revokeUserAdmin(USER_ID); expect.that(mUms.revokeUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test public void testRevokeUserAdminFromNonAdmin() { addSecondaryUser(USER_ID); mUms.revokeUserAdmin(USER_ID); expect.that(mUms.revokeUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } Loading @@ -1877,16 +1877,41 @@ public final class UserManagerServiceMockedTest { @Test @EnableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_nonHsum_hsuNotAdmin() { testRevokeUserAdminFailsForSystemUser_nonHsum(); setSystemUserHeadless(false); testRevokeAdminFromSystemUser(/* allowed= */ false); } @Test @DisableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_nonHsum() { setSystemUserHeadless(false); mUms.revokeUserAdmin(UserHandle.USER_SYSTEM); testRevokeAdminFromSystemUser(/* allowed= */ false); } @Test @EnableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminSucceedsForSystemUser_hsum_hsuNotAdmin() { setSystemUserHeadless(true); testRevokeAdminFromSystemUser(/* allowed= */ true); } @Test @DisableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_hsum() { setSystemUserHeadless(true); testRevokeAdminFromSystemUser(/* allowed= */ false); } private void testRevokeAdminFromSystemUser(boolean allowed) { UserInfo info = mUsers.get(UserHandle.USER_SYSTEM).info; // Whether or not it's and admin depends on FLAG_HSU_NOT_ADMIN boolean isAdminBefore = info.isAdmin(); boolean result = mUms.revokeUserAdminInternal(UserHandle.USER_SYSTEM); assertThat(mUsers.get(UserHandle.USER_SYSTEM).info.isAdmin()).isTrue(); expect.withMessage("revokeUserAdmin(USER_SYSTEM)").that(result).isEqualTo(allowed); expect.withMessage("USER_SYSTEM.isAdmin() after revokeUserAdmin(...)") .that(info.isAdmin()).isEqualTo(isAdminBefore); } @Test Loading Loading
services/core/java/com/android/server/pm/UserManagerService.java +30 −8 Original line number Diff line number Diff line Loading @@ -2390,6 +2390,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setUserAdmin(@UserIdInt int userId) { setUserAdminInternal(userId); } // NOTE: split in 2 methods because cmd user needs a boolean but the AIDL one doesn't boolean setUserAdminInternal(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); Loading @@ -2405,30 +2410,41 @@ public class UserManagerService extends IUserManager.Stub { // Exit if no user found with that id, mUserJourneyLogger.logNullUserJourneyError(USER_JOURNEY_GRANT_ADMIN, currentUserId, userId, /* userType */ "", /* userFlags */ -1); return; Slogf.w(LOG_TAG, "setUserAdmin(%d) failed: user not found", userId); return false; } else if (user.info.isAdmin()) { // Exit if the user is already an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_USER_ALREADY_AN_ADMIN); return; Slogf.w(LOG_TAG, "setUserAdmin(%d): not changed, already admin", userId); return true; } else if (user.info.isProfile() || user.info.isGuest() || user.info.isRestricted()) { // Profiles, guest users or restricted profiles cannot become an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_INVALID_USER_TYPE); return; Slogf.w(LOG_TAG, "setUserAdmin(%d) failed: profile, guest, and restricted users" + " cannot be admins (user: %s) ", userId, user.info.toFullString()); return false; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); } } Slogf.i(LOG_TAG, "setUserAdmin(%d): succeeded", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_GRANT_ADMIN, ERROR_CODE_UNSPECIFIED); return true; } @Override public void revokeUserAdmin(@UserIdInt int userId) { revokeUserAdminInternal(userId); } // NOTE: split in 2 methods because cmd user needs a boolean but the AIDL one doesn't boolean revokeUserAdminInternal(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("revoke admin privileges"); if (Flags.unicornModeRefactoringForHsumReadOnly()) { checkAdminStatusChangeAllowed(userId); Loading @@ -2444,23 +2460,27 @@ public class UserManagerService extends IUserManager.Stub { // Exit if no user found with that id mUserJourneyLogger.logNullUserJourneyError( USER_JOURNEY_REVOKE_ADMIN, currentUserId, userId, "", -1); return; Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: user not found", userId); return false; } else if (!user.info.isAdmin()) { // Exit if user is not an admin. mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_NOT_AN_ADMIN); return; Slogf.w(LOG_TAG, "revokeUserAdmin(%d): not changed, already not admin", userId); return true; } else if ((user.info.flags & UserInfo.FLAG_SYSTEM) != 0) { // System user cannot lose its admin status. Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: system user", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_INVALID_USER_TYPE); return; return false; } else if (isNonRemovableLastAdminUserLU(user.info)) { // This is the last admin user and this device requires that it not lose its // admin status. Slogf.w(LOG_TAG, "revokeUserAdmin(%d) failed: user is last admin", userId); mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_USER_IS_LAST_ADMIN); return; return false; } user.info.flags ^= UserInfo.FLAG_ADMIN; writeUserLP(user); Loading @@ -2468,6 +2488,8 @@ public class UserManagerService extends IUserManager.Stub { } mUserJourneyLogger.logUserJourneyFinishWithError(currentUserId, user.info, USER_JOURNEY_REVOKE_ADMIN, ERROR_CODE_UNSPECIFIED); Slogf.i(LOG_TAG, "revokeUserAdmin(%d): succeeded", userId); return true; } /** Loading
services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java +24 −18 Original line number Diff line number Diff line Loading @@ -126,11 +126,11 @@ public class UserManagerServiceShellCommand extends ShellCommand { pw.println(" get-main-user "); pw.println(" Displays main user id or message if there is no main user"); pw.println(); pw.println(" set-user-admin <USER_ID>"); pw.println(" Sets the given user as an admin"); pw.println(" grant-admin <USER_ID>"); pw.println(" Grants admin privileges to the given user (requires adb root)"); pw.println(); pw.println(" revoke-user-admin <USER_ID>"); pw.println(" Revokes the given user as an admin"); pw.println(" revoke-admin <USER_ID>"); pw.println(" Revokes admin privileges from the given user (requires adb root)"); pw.println(); } Loading Loading @@ -162,10 +162,10 @@ public class UserManagerServiceShellCommand extends ShellCommand { return runCanSwitchToHeadlessSystemUser(); case "is-main-user-permanent-admin": return runIsMainUserPermanentAdmin(); case "set-user-admin": return runSetUserAdmin(); case "revoke-user-admin": return runRevokeUserAdmin(); case "grant-admin": return runGrantAdmin(); case "revoke-admin": return runRevokeAdmin(); default: return handleDefaultCommands(cmd); } Loading Loading @@ -570,15 +570,15 @@ public class UserManagerServiceShellCommand extends ShellCommand { return 0; } private int runSetUserAdmin() throws RemoteException { return setOrRevokeAdmin(/* set= */ true); private int runGrantAdmin() throws RemoteException { return grantOrRevokeAdmin(/* grant= */ true); } private int runRevokeUserAdmin() throws RemoteException { return setOrRevokeAdmin(/* set= */ false); private int runRevokeAdmin() throws RemoteException { return grantOrRevokeAdmin(/* grant= */ false); } private int setOrRevokeAdmin(boolean set) { private int grantOrRevokeAdmin(boolean grant) { if (!confirmBuildIsDebuggable() || !confirmIsCalledByRoot()) { return -1; } Loading @@ -586,15 +586,21 @@ public class UserManagerServiceShellCommand extends ShellCommand { if (userId == UserHandle.USER_NULL) { return -1; } if (set) { boolean success; if (grant) { Slogf.i(LOG_TAG, "Calling setUserAdmin(%d)", userId); mService.setUserAdmin(userId); success = mService.setUserAdminInternal(userId); } else { Slogf.i(LOG_TAG, "Calling revokeUserAdmin(%d)", userId); mService.revokeUserAdmin(userId); success = mService.revokeUserAdminInternal(userId); } if (success) { getOutPrintWriter().println("Success"); return 0; } else { getOutPrintWriter().println("Failed"); return -1; } } /** Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +39 −14 Original line number Diff line number Diff line Loading @@ -1789,9 +1789,9 @@ public final class UserManagerServiceMockedTest { public void testSetUserAdmin() { addSecondaryUser(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isTrue(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isTrue(); } @Test Loading @@ -1815,9 +1815,9 @@ public final class UserManagerServiceMockedTest { public void testSetUserAdminFailsForGuest() { addGuestUser(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isFalse(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test Loading @@ -1825,34 +1825,34 @@ public final class UserManagerServiceMockedTest { addSecondaryUser(PARENT_USER_ID); addProfile(PROFILE_USER_ID, PARENT_USER_ID, USER_TYPE_PROFILE_MANAGED); mUms.setUserAdmin(PROFILE_USER_ID); expect.that(mUms.setUserAdminInternal(PROFILE_USER_ID)).isFalse(); assertThat(mUsers.get(PROFILE_USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(PROFILE_USER_ID).info.isAdmin()).isFalse(); } @Test public void testSetUserAdminFailsForRestrictedProfile() { addRestrictedProfile(USER_ID); mUms.setUserAdmin(USER_ID); expect.that(mUms.setUserAdminInternal(USER_ID)).isFalse(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test public void testRevokeUserAdmin() { addAdminUser(USER_ID); mUms.revokeUserAdmin(USER_ID); expect.that(mUms.revokeUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); expect.that(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } @Test public void testRevokeUserAdminFromNonAdmin() { addSecondaryUser(USER_ID); mUms.revokeUserAdmin(USER_ID); expect.that(mUms.revokeUserAdminInternal(USER_ID)).isTrue(); assertThat(mUsers.get(USER_ID).info.isAdmin()).isFalse(); } Loading @@ -1877,16 +1877,41 @@ public final class UserManagerServiceMockedTest { @Test @EnableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_nonHsum_hsuNotAdmin() { testRevokeUserAdminFailsForSystemUser_nonHsum(); setSystemUserHeadless(false); testRevokeAdminFromSystemUser(/* allowed= */ false); } @Test @DisableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_nonHsum() { setSystemUserHeadless(false); mUms.revokeUserAdmin(UserHandle.USER_SYSTEM); testRevokeAdminFromSystemUser(/* allowed= */ false); } @Test @EnableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminSucceedsForSystemUser_hsum_hsuNotAdmin() { setSystemUserHeadless(true); testRevokeAdminFromSystemUser(/* allowed= */ true); } @Test @DisableFlags(FLAG_HSU_NOT_ADMIN) public void testRevokeUserAdminFailsForSystemUser_hsum() { setSystemUserHeadless(true); testRevokeAdminFromSystemUser(/* allowed= */ false); } private void testRevokeAdminFromSystemUser(boolean allowed) { UserInfo info = mUsers.get(UserHandle.USER_SYSTEM).info; // Whether or not it's and admin depends on FLAG_HSU_NOT_ADMIN boolean isAdminBefore = info.isAdmin(); boolean result = mUms.revokeUserAdminInternal(UserHandle.USER_SYSTEM); assertThat(mUsers.get(UserHandle.USER_SYSTEM).info.isAdmin()).isTrue(); expect.withMessage("revokeUserAdmin(USER_SYSTEM)").that(result).isEqualTo(allowed); expect.withMessage("USER_SYSTEM.isAdmin() after revokeUserAdmin(...)") .that(info.isAdmin()).isEqualTo(isAdminBefore); } @Test Loading