Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0b86b17 authored by Yao Li's avatar Yao Li
Browse files

Add getUserRemovability() to UserManager

Change to existing getUserRemovabilityLockedLU() is mechanical.

Bug: 394970894
Flag: android.multiuser.disallow_removing_last_admin_user
Test: atest FrameworksMockingServicesTests:UserManagerServiceMockedTest
Change-Id: I237c201ad056d41f1b6936dbb5902c33dc3ded84
parent 371470ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+30 −0
Original line number Diff line number Diff line
@@ -6423,6 +6423,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.
     *
+40 −13
Original line number Diff line number Diff line
@@ -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;
                }
@@ -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);
            }
@@ -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);
                    }
@@ -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);

@@ -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.
+1 −1
Original line number Diff line number Diff line
@@ -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),