Loading core/java/android/os/IUserManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ interface IUserManager { void evictCredentialEncryptionKey(int userId); boolean removeUser(int userId); boolean removeUserEvenWhenDisallowed(int userId); int getUserRemovability(int userId); void setUserName(int userId, String name); void setUserIcon(int userId, in Bitmap icon); ParcelFileDescriptor getUserIcon(int userId); Loading core/java/android/os/UserManager.java +30 −0 Original line number Diff line number Diff line Loading @@ -6442,6 +6442,36 @@ public class UserManager { return result >= 0; } /** * Returns whether the specified user is removable. * * <p>Removing a user is not allowed in the following cases: * <ol> * <li>the user is system user * <li>the user is not found * <li>the user is permanent admin main user * <li>the user is already being removed * <li>the user is a non-removable last admin user * * </ol> * * @return A {@link RemoveResult} flag indicating if the user can be removed, one of * {@link #REMOVE_RESULT_USER_IS_REMOVABLE}, * {@link #REMOVE_RESULT_ERROR_SYSTEM_USER}, * {@link #REMOVE_RESULT_ERROR_USER_NOT_FOUND}, * {@link #REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN}, * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, * {@link #REMOVE_RESULT_ERROR_LAST_ADMIN_USER}. * @hide */ public @RemoveResult int getUserRemovability(@UserIdInt int userId) { try { return mService.getUserRemovability(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Updates the user's name. * Loading services/core/java/com/android/server/pm/UserManagerService.java +40 −13 Original line number Diff line number Diff line Loading @@ -3505,8 +3505,8 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "set as ephemeral"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "set as ephemeral"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return userRemovability; } Loading Loading @@ -6942,7 +6942,8 @@ public class UserManagerService extends IUserManager.Stub { final boolean isProfile; final IntArray profileIds; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "removed"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } Loading Loading @@ -7028,7 +7029,8 @@ public class UserManagerService extends IUserManager.Stub { final UserData userData; synchronized (mPackagesLock) { synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "removed"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } Loading Loading @@ -7156,34 +7158,48 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy("mUsersLock") @VisibleForTesting @UserManager.RemoveResult int getUserRemovabilityLockedLU(@UserIdInt int userId, String msg) { @UserManager.RemoveResult int getUserRemovabilityLockedLU(@UserIdInt int userId) { if (userId == UserHandle.USER_SYSTEM) { Slogf.e(LOG_TAG, "User %d can not be %s, system user cannot be removed.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER; } final UserData userData = mUsers.get(userId); if (userData == null) { Slogf.e(LOG_TAG, "User %d can not be %s, invalid user id provided.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND; } if (isNonRemovableMainUser(userData.info)) { Slogf.e(LOG_TAG, "User %d can not be %s, main user cannot be removed when it's a" + " permanent admin user.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN; } if (mRemovingUserIds.get(userId)) { Slogf.w(LOG_TAG, "User %d can not be %s, it is already scheduled for removal.", userId, msg); return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; } if (isNonRemovableLastAdminUserLU(userData.info)) { Slogf.e(LOG_TAG, "User %d can not be %s, last admin user cannot be removed.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_LAST_ADMIN_USER; } return UserManager.REMOVE_RESULT_USER_IS_REMOVABLE; } private void logRemoveResultError(@UserManager.RemoveResult int result, @UserIdInt int userId, String action) { switch (result) { case UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER -> Slogf.e(LOG_TAG, "User %d can not be %s, system user cannot be removed.", userId, action); case UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND -> Slogf.e(LOG_TAG, "User %d can not be %s, invalid user id provided.", userId, action); case UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN -> Slogf.e(LOG_TAG, "User %d can not be %s, main user cannot be removed when it's a" + " permanent admin user.", userId, action); case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED -> Slogf.w(LOG_TAG, "User %d can not be %s, it is already scheduled for removal.", userId, action); case UserManager.REMOVE_RESULT_ERROR_LAST_ADMIN_USER -> Slogf.e(LOG_TAG, "User %d can not be %s, last admin user cannot be removed.", userId, action); default -> {} } } private void finishRemoveUser(final @UserIdInt int userId) { Slog.i(LOG_TAG, "finishRemoveUser " + userId); Loading Loading @@ -9053,6 +9069,17 @@ public class UserManagerService extends IUserManager.Stub { return userInfo.isMain() && isMainUserPermanentAdmin(); } @Override public @UserManager.RemoveResult int getUserRemovability(@UserIdInt int userId) { if (!Flags.disallowRemovingLastAdminUser()) { throw new UnsupportedOperationException( "aconfig flag android.multiuser.disallow_removing_last_admin_user not enabled"); } synchronized (mUsersLock) { return getUserRemovabilityLockedLU(userId); } } /** * Returns true if we must not delete this user or revoke its admin status because it is the * last admin user on a device that requires there to always be at least one admin. Loading services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -2425,7 +2425,7 @@ public final class UserManagerServiceMockedTest { private void expectGetUserRemovability(String who, @UserIdInt int userId, @RemoveResult int expectedResult) { int actualResult = mUms.getUserRemovabilityLockedLU(userId, /* msg= */ "not used"); int actualResult = mUms.getUserRemovabilityLockedLU(userId); expect.withMessage("getUserRemovabilityLockedLU(%s) (where user is %s, %s=%s, and %s=%s)", who, userId, expectedResult, removeResultToString(expectedResult), Loading Loading
core/java/android/os/IUserManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ interface IUserManager { void evictCredentialEncryptionKey(int userId); boolean removeUser(int userId); boolean removeUserEvenWhenDisallowed(int userId); int getUserRemovability(int userId); void setUserName(int userId, String name); void setUserIcon(int userId, in Bitmap icon); ParcelFileDescriptor getUserIcon(int userId); Loading
core/java/android/os/UserManager.java +30 −0 Original line number Diff line number Diff line Loading @@ -6442,6 +6442,36 @@ public class UserManager { return result >= 0; } /** * Returns whether the specified user is removable. * * <p>Removing a user is not allowed in the following cases: * <ol> * <li>the user is system user * <li>the user is not found * <li>the user is permanent admin main user * <li>the user is already being removed * <li>the user is a non-removable last admin user * * </ol> * * @return A {@link RemoveResult} flag indicating if the user can be removed, one of * {@link #REMOVE_RESULT_USER_IS_REMOVABLE}, * {@link #REMOVE_RESULT_ERROR_SYSTEM_USER}, * {@link #REMOVE_RESULT_ERROR_USER_NOT_FOUND}, * {@link #REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN}, * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, * {@link #REMOVE_RESULT_ERROR_LAST_ADMIN_USER}. * @hide */ public @RemoveResult int getUserRemovability(@UserIdInt int userId) { try { return mService.getUserRemovability(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Updates the user's name. * Loading
services/core/java/com/android/server/pm/UserManagerService.java +40 −13 Original line number Diff line number Diff line Loading @@ -3505,8 +3505,8 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "set as ephemeral"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "set as ephemeral"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return userRemovability; } Loading Loading @@ -6942,7 +6942,8 @@ public class UserManagerService extends IUserManager.Stub { final boolean isProfile; final IntArray profileIds; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "removed"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } Loading Loading @@ -7028,7 +7029,8 @@ public class UserManagerService extends IUserManager.Stub { final UserData userData; synchronized (mPackagesLock) { synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLockedLU(userId, "removed"); final int userRemovability = getUserRemovabilityLockedLU(userId); logRemoveResultError(userRemovability, userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } Loading Loading @@ -7156,34 +7158,48 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy("mUsersLock") @VisibleForTesting @UserManager.RemoveResult int getUserRemovabilityLockedLU(@UserIdInt int userId, String msg) { @UserManager.RemoveResult int getUserRemovabilityLockedLU(@UserIdInt int userId) { if (userId == UserHandle.USER_SYSTEM) { Slogf.e(LOG_TAG, "User %d can not be %s, system user cannot be removed.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER; } final UserData userData = mUsers.get(userId); if (userData == null) { Slogf.e(LOG_TAG, "User %d can not be %s, invalid user id provided.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND; } if (isNonRemovableMainUser(userData.info)) { Slogf.e(LOG_TAG, "User %d can not be %s, main user cannot be removed when it's a" + " permanent admin user.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN; } if (mRemovingUserIds.get(userId)) { Slogf.w(LOG_TAG, "User %d can not be %s, it is already scheduled for removal.", userId, msg); return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; } if (isNonRemovableLastAdminUserLU(userData.info)) { Slogf.e(LOG_TAG, "User %d can not be %s, last admin user cannot be removed.", userId, msg); return UserManager.REMOVE_RESULT_ERROR_LAST_ADMIN_USER; } return UserManager.REMOVE_RESULT_USER_IS_REMOVABLE; } private void logRemoveResultError(@UserManager.RemoveResult int result, @UserIdInt int userId, String action) { switch (result) { case UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER -> Slogf.e(LOG_TAG, "User %d can not be %s, system user cannot be removed.", userId, action); case UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND -> Slogf.e(LOG_TAG, "User %d can not be %s, invalid user id provided.", userId, action); case UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN -> Slogf.e(LOG_TAG, "User %d can not be %s, main user cannot be removed when it's a" + " permanent admin user.", userId, action); case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED -> Slogf.w(LOG_TAG, "User %d can not be %s, it is already scheduled for removal.", userId, action); case UserManager.REMOVE_RESULT_ERROR_LAST_ADMIN_USER -> Slogf.e(LOG_TAG, "User %d can not be %s, last admin user cannot be removed.", userId, action); default -> {} } } private void finishRemoveUser(final @UserIdInt int userId) { Slog.i(LOG_TAG, "finishRemoveUser " + userId); Loading Loading @@ -9053,6 +9069,17 @@ public class UserManagerService extends IUserManager.Stub { return userInfo.isMain() && isMainUserPermanentAdmin(); } @Override public @UserManager.RemoveResult int getUserRemovability(@UserIdInt int userId) { if (!Flags.disallowRemovingLastAdminUser()) { throw new UnsupportedOperationException( "aconfig flag android.multiuser.disallow_removing_last_admin_user not enabled"); } synchronized (mUsersLock) { return getUserRemovabilityLockedLU(userId); } } /** * Returns true if we must not delete this user or revoke its admin status because it is the * last admin user on a device that requires there to always be at least one admin. Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -2425,7 +2425,7 @@ public final class UserManagerServiceMockedTest { private void expectGetUserRemovability(String who, @UserIdInt int userId, @RemoveResult int expectedResult) { int actualResult = mUms.getUserRemovabilityLockedLU(userId, /* msg= */ "not used"); int actualResult = mUms.getUserRemovabilityLockedLU(userId); expect.withMessage("getUserRemovabilityLockedLU(%s) (where user is %s, %s=%s, and %s=%s)", who, userId, expectedResult, removeResultToString(expectedResult), Loading