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

Commit 0325034c authored by Nikhil Kumar's avatar Nikhil Kumar Committed by Android (Google) Code Review
Browse files

Merge "Remove associated profiles when removing a user"

parents 8b7bf7ce d4a00138
Loading
Loading
Loading
Loading
+5 −5
Original line number Original line Diff line number Diff line
@@ -4993,7 +4993,7 @@ public class UserManager {
    }
    }


    /**
    /**
     * Removes a user and all associated data.
     * Removes a user and its profiles along with their associated data.
     * @param userId the integer handle of the user.
     * @param userId the integer handle of the user.
     * @hide
     * @hide
     */
     */
@@ -5009,7 +5009,7 @@ public class UserManager {
    }
    }


    /**
    /**
     * Removes a user and all associated data.
     * Removes a user and its profiles along with their associated data.
     *
     *
     * @param user the user that needs to be removed.
     * @param user the user that needs to be removed.
     * @return {@code true} if the user was successfully removed, {@code false} otherwise.
     * @return {@code true} if the user was successfully removed, {@code false} otherwise.
@@ -5046,9 +5046,9 @@ public class UserManager {
    }
    }


    /**
    /**
     * Immediately removes the user or, if the user cannot be removed, such as when the user is
     * Immediately removes the user and its profiles or, if the user cannot be removed, such as
     * the current user, then set the user as ephemeral so that it will be removed when it is
     * when the user is the current user, then set the user as ephemeral
     * stopped.
     * so that it will be removed when it is stopped.
     *
     *
     * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
     * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
     * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
     * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
+47 −7
Original line number Original line Diff line number Diff line
@@ -5199,8 +5199,9 @@ public class UserManagerService extends IUserManager.Stub {
    }
    }


    /**
    /**
     * Removes a user and all data directories created for that user. This method should be called
     * Removes a user and its profiles along with all data directories created for that user
     * after the user's processes have been terminated.
     * and its profile.
     * This method should be called after the user's processes have been terminated.
     * @param userId the user's id
     * @param userId the user's id
     */
     */
    @Override
    @Override
@@ -5213,13 +5214,52 @@ public class UserManagerService extends IUserManager.Stub {
            Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
            Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
            return false;
            return false;
        }
        }
        return removeUserWithProfilesUnchecked(userId);
    }

    private boolean removeUserWithProfilesUnchecked(@UserIdInt int userId) {
        UserInfo userInfo = getUserInfoNoChecks(userId);

        if (userInfo == null) {
            Slog.e(LOG_TAG, TextUtils.formatSimple(
                    "Cannot remove user %d, invalid user id provided.", userId));
            return false;
        }

        if (!userInfo.isProfile()) {
            int[] profileIds = getProfileIds(userId, false);
            for (int profileId : profileIds) {
                if (profileId == userId) {
                    //Remove the associated profiles first and then remove the user
                    continue;
                }
                Slog.i(LOG_TAG, "removing profile:" + profileId
                        + "associated with user:" + userId);
                if (!removeUserUnchecked(profileId)) {
                    // If the profile 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 profile " + profileId
                            + "associated with user " + userId + ". User is set as ephemeral "
                            + "and will be removed on user switch or reboot.");
                    synchronized (mPackagesLock) {
                        UserData profileData = getUserDataNoChecks(userId);
                        profileData.info.flags |= UserInfo.FLAG_EPHEMERAL;

                        writeUserLP(profileData);
                    }
                }
            }
        }

        return removeUserUnchecked(userId);
        return removeUserUnchecked(userId);
    }
    }


    @Override
    @Override
    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
        checkCreateUsersPermission("Only the system can remove users");
        checkCreateUsersPermission("Only the system can remove users");
        return removeUserUnchecked(userId);
        return removeUserWithProfilesUnchecked(userId);
    }
    }


    /**
    /**
@@ -5255,13 +5295,13 @@ public class UserManagerService extends IUserManager.Stub {
                    }
                    }


                    if (userData == null) {
                    if (userData == null) {
                        Slog.e(LOG_TAG, String.format(
                        Slog.e(LOG_TAG, TextUtils.formatSimple(
                                "Cannot remove user %d, invalid user id provided.", userId));
                                "Cannot remove user %d, invalid user id provided.", userId));
                        return false;
                        return false;
                    }
                    }


                    if (mRemovingUserIds.get(userId)) {
                    if (mRemovingUserIds.get(userId)) {
                        Slog.e(LOG_TAG, String.format(
                        Slog.e(LOG_TAG, TextUtils.formatSimple(
                                "User %d is already scheduled for removal.", userId));
                                "User %d is already scheduled for removal.", userId));
                        return false;
                        return false;
                    }
                    }
@@ -5372,7 +5412,7 @@ public class UserManagerService extends IUserManager.Stub {
                final int currentUser = getCurrentUserId();
                final int currentUser = getCurrentUserId();
                if (currentUser != userId) {
                if (currentUser != userId) {
                    // Attempt to remove the user. This will fail if the user is the current user
                    // Attempt to remove the user. This will fail if the user is the current user
                    if (removeUserUnchecked(userId)) {
                    if (removeUserWithProfilesUnchecked(userId)) {
                        return UserManager.REMOVE_RESULT_REMOVED;
                        return UserManager.REMOVE_RESULT_REMOVED;
                    }
                    }
                }
                }
@@ -6618,7 +6658,7 @@ public class UserManagerService extends IUserManager.Stub {


        @Override
        @Override
        public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
        public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
            return removeUserUnchecked(userId);
            return removeUserWithProfilesUnchecked(userId);
        }
        }


        @Override
        @Override
+40 −17
Original line number Original line Diff line number Diff line
@@ -284,22 +284,8 @@ public final class UserManagerTest {
    @Test
    @Test
    public void testRemoveUserByHandle() {
    public void testRemoveUserByHandle() {
        UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
        UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
        final UserHandle user = userInfo.getUserHandle();

        synchronized (mUserRemoveLock) {
        removeUser(userInfo.getUserHandle());
            mUserManager.removeUser(user);
            long time = System.currentTimeMillis();
            while (mUserManager.getUserInfo(user.getIdentifier()) != null) {
                try {
                    mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    return;
                }
                if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
                    fail("Timeout waiting for removeUser. userId = " + user.getIdentifier());
                }
            }
        }


        assertThat(hasUser(userInfo.id)).isFalse();
        assertThat(hasUser(userInfo.id)).isFalse();
    }
    }
@@ -307,7 +293,7 @@ public final class UserManagerTest {
    @MediumTest
    @MediumTest
    @Test
    @Test
    public void testRemoveUserByHandle_ThrowsException() {
    public void testRemoveUserByHandle_ThrowsException() {
        assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null));
        assertThrows(IllegalArgumentException.class, () -> removeUser(null));
    }
    }


    @MediumTest
    @MediumTest
@@ -410,6 +396,30 @@ public final class UserManagerTest {
        assertThat(hasUser(user1.id)).isFalse();
        assertThat(hasUser(user1.id)).isFalse();
    }
    }


    @MediumTest
    @Test
    public void testRemoveUserWhenPossible_withProfiles() throws Exception {
        assumeHeadlessModeEnabled();
        final UserInfo parentUser = createUser("Human User", /* flags= */ 0);
        final UserInfo cloneProfileUser = createProfileForUser("Clone Profile user",
                UserManager.USER_TYPE_PROFILE_CLONE,
                parentUser.id);

        final UserInfo workProfileUser = createProfileForUser("Work Profile user",
                UserManager.USER_TYPE_PROFILE_MANAGED,
                parentUser.id);
        synchronized (mUserRemoveLock) {
            assertThat(mUserManager.removeUserWhenPossible(parentUser.getUserHandle(),
                    /* overrideDevicePolicy= */ false))
                    .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
            waitForUserRemovalLocked(parentUser.id);
        }

        assertThat(hasUser(parentUser.id)).isFalse();
        assertThat(hasUser(cloneProfileUser.id)).isFalse();
        assertThat(hasUser(workProfileUser.id)).isFalse();
    }

    /** Tests creating a FULL user via specifying userType. */
    /** Tests creating a FULL user via specifying userType. */
    @MediumTest
    @MediumTest
    @Test
    @Test
@@ -1182,6 +1192,13 @@ public final class UserManagerTest {
        }
        }
    }
    }


    private void removeUser(UserHandle user) {
        synchronized (mUserRemoveLock) {
            mUserManager.removeUser(user);
            waitForUserRemovalLocked(user.getIdentifier());
        }
    }

    private void removeUser(int userId) {
    private void removeUser(int userId) {
        synchronized (mUserRemoveLock) {
        synchronized (mUserRemoveLock) {
            mUserManager.removeUser(userId);
            mUserManager.removeUser(userId);
@@ -1261,6 +1278,12 @@ public final class UserManagerTest {
                && (!isAutomotive() || !UserManager.isHeadlessSystemUserMode()));
                && (!isAutomotive() || !UserManager.isHeadlessSystemUserMode()));
    }
    }


    private void assumeHeadlessModeEnabled() {
        // assume headless mode is enabled
        assumeTrue("Device doesn't have headless mode enabled",
                UserManager.isHeadlessSystemUserMode());
    }

    private boolean isAutomotive() {
    private boolean isAutomotive() {
        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
    }
    }