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

Commit dc7852e2 authored by Yasin Kilicdere's avatar Yasin Kilicdere Committed by Android (Google) Code Review
Browse files

Merge "Prevent removing the current user or the target user of user switch."

parents 61978f2d 85da667c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -311,6 +311,12 @@ public abstract class ActivityManagerInternal {
    @PermissionMethod
    public abstract void enforceCallingPermission(@PermissionName String permission, String func);

    /**
     * Returns the current and target user ids as a {@link Pair}. Target user id will be
     * {@link android.os.UserHandle#USER_NULL} if there is not an ongoing user switch.
     */
    public abstract Pair<Integer, Integer> getCurrentAndTargetUserIds();

    /** Returns the current user id. */
    public abstract int getCurrentUserId();

+5 −0
Original line number Diff line number Diff line
@@ -17363,6 +17363,11 @@ public class ActivityManagerService extends IActivityManager.Stub
            ActivityManagerService.this.enforceCallingPermission(permission, func);
        }
        @Override
        public Pair<Integer, Integer> getCurrentAndTargetUserIds() {
            return mUserController.getCurrentAndTargetUserIds();
        }
        @Override
        public int getCurrentUserId() {
            return mUserController.getCurrentUserId();
+6 −0
Original line number Diff line number Diff line
@@ -2742,6 +2742,12 @@ class UserController implements Handler.Callback {
        return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
    }

    Pair<Integer, Integer> getCurrentAndTargetUserIds() {
        synchronized (mLock) {
            return new Pair<>(mCurrentUserId, mTargetUserId);
        }
    }

    @GuardedBy("mLock")
    private int getCurrentUserIdLU() {
        return mCurrentUserId;
+40 −8
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -1834,6 +1835,27 @@ public class UserManagerService extends IUserManager.Stub {
        return mUserVisibilityMediator.isUserVisible(userId);
    }

    /**
     * Gets the current and target user ids as a {@link Pair}, calling
     * {@link ActivityManagerInternal} directly (and without performing any permission check).
     *
     * @return ids of current foreground user and the target user. Target user will be
     * {@link UserHandle#USER_NULL} if there is not an ongoing user switch. And if
     * {@link ActivityManagerInternal} is not available yet, they will both be
     * {@link UserHandle#USER_NULL}.
     */
    @VisibleForTesting
    @NonNull
    Pair<Integer, Integer> getCurrentAndTargetUserIds() {
        ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
        if (activityManagerInternal == null) {
            Slog.w(LOG_TAG, "getCurrentAndTargetUserId() called too early, "
                    + "ActivityManagerInternal is not set yet");
            return new Pair<>(UserHandle.USER_NULL, UserHandle.USER_NULL);
        }
        return activityManagerInternal.getCurrentAndTargetUserIds();
    }

    /**
     * Gets the current user id, calling {@link ActivityManagerInternal} directly (and without
     * performing any permission check).
@@ -5426,11 +5448,15 @@ public class UserManagerService extends IUserManager.Stub {
        final long ident = Binder.clearCallingIdentity();
        try {
            final UserData userData;
            int currentUser = getCurrentUserId();
            if (currentUser == userId) {
            Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds();
            if (userId == currentAndTargetUserIds.first) {
                Slog.w(LOG_TAG, "Current user cannot be removed.");
                return false;
            }
            if (userId == currentAndTargetUserIds.second) {
                Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed.");
                return false;
            }
            synchronized (mPackagesLock) {
                synchronized (mUsersLock) {
                    userData = mUsers.get(userId);
@@ -5567,9 +5593,10 @@ public class UserManagerService extends IUserManager.Stub {
                    }
                }

                // Attempt to immediately remove a non-current user
                final int currentUser = getCurrentUserId();
                if (currentUser != userId) {
                // Attempt to immediately remove a non-current and non-target user
                Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds();
                if (userId != currentAndTargetUserIds.first
                        && userId != currentAndTargetUserIds.second) {
                    // Attempt to remove the user. This will fail if the user is the current user
                    if (removeUserWithProfilesUnchecked(userId)) {
                        return UserManager.REMOVE_RESULT_REMOVED;
@@ -5578,9 +5605,14 @@ public class UserManagerService extends IUserManager.Stub {
                // If the user was not immediately removed, make sure it is marked as ephemeral.
                // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an
                // ephemeral user should only be marked as disabled when its removal is in progress.
                Slog.i(LOG_TAG, "Unable to immediately remove user " + userId + " (current user is "
                        + currentUser + "). User is set as ephemeral and will be removed on user "
                        + "switch or reboot.");
                Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d "
                                + "(%s is %d). User is set as ephemeral and will be removed on "
                                + "user switch or reboot.",
                        userId,
                        userId == currentAndTargetUserIds.first
                                ? "current user"
                                : "target user of an ongoing user switch",
                        userId));
                userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
                writeUserLP(userData);

+18 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.UserManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;

import androidx.test.annotation.UiThreadTest;
@@ -153,6 +154,15 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
                .isEqualTo(UserHandle.USER_NULL);
    }

    @Test
    public void testGetCurrentAndTargetUserIds() {
        mockCurrentAndTargetUser(USER_ID, OTHER_USER_ID);

        assertWithMessage("getCurrentAndTargetUserIds()")
                .that(mUms.getCurrentAndTargetUserIds())
                .isEqualTo(new Pair<>(USER_ID, OTHER_USER_ID));
    }

    @Test
    public void testGetCurrentUserId() {
        mockCurrentUser(USER_ID);
@@ -329,6 +339,14 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
        when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId);
    }

    private void mockCurrentAndTargetUser(@UserIdInt int currentUserId,
            @UserIdInt int targetUserId) {
        mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);

        when(mActivityManagerInternal.getCurrentAndTargetUserIds())
                .thenReturn(new Pair<>(currentUserId, targetUserId));
    }

    private <T> void mockGetLocalService(Class<T> serviceClass, T service) {
        doReturn(service).when(() -> LocalServices.getService(serviceClass));
    }
Loading