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

Commit bf7a9aca authored by Adam Bookatz's avatar Adam Bookatz
Browse files

Reschedule stop background users if Guest current

If the current user is a Guest, don't stop background users due to their
having been scheduled for stopping-due-to-being-in-background. Instead,
reschdule their stopping for later.

The idea is: Don't kill a background user just for the sake of a Guest.

Fixes: 335739360
Test: atest UserControllerTest#testScheduleStopOfBackgroundUser_rescheduleWhenGuest
Test: atest UserControllerTest
Flag: ACONFIG android.multiuser.scheduleStopOfBackgroundUser
Change-Id: I4f08e654e608898e55c6c84d3fe07917f1b137d0
parent e89f7097
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -2333,6 +2333,14 @@ class UserController implements Handler.Callback {
            // Never stop system user
            return;
        }
        synchronized(mLock) {
            final UserState uss = mStartedUsers.get(oldUserId);
            if (uss == null || uss.state == UserState.STATE_STOPPING
                    || uss.state == UserState.STATE_SHUTDOWN) {
                // We've stopped (or are stopping) the user anyway, so don't bother scheduling.
                return;
            }
        }
        if (oldUserId == mInjector.getUserManagerInternal().getMainUserId()) {
            // MainUser is currently special for things like Docking, so we'll exempt it for now.
            Slogf.i(TAG, "Exempting user %d from being stopped due to inactivity by virtue "
@@ -2369,6 +2377,12 @@ class UserController implements Handler.Callback {
                // We'll soon want to switch to this user, so don't kill it now.
                return;
            }
            final UserInfo currentOrTargetUser = getCurrentUserLU();
            if (currentOrTargetUser != null && currentOrTargetUser.isGuest()) {
                // Don't kill any background users for the sake of a Guest. Just reschedule instead.
                scheduleStopOfBackgroundUser(userId);
                return;
            }
            Slogf.i(TAG, "Stopping background user %d due to inactivity", userId);
            stopUsersLU(userId, /* allowDelayedLocking= */ true, null, null);
        }
+1 −0
Original line number Diff line number Diff line
@@ -5906,6 +5906,7 @@ public class UserManagerService extends IUserManager.Stub {
        return userData;
    }

    /** For testing only! Directly, unnaturally removes userId from list of users. */
    @VisibleForTesting
    void removeUserInfo(@UserIdInt int userId) {
        synchronized (mUsersLock) {
+67 −5
Original line number Diff line number Diff line
@@ -672,6 +672,61 @@ public class UserControllerTest {
                new HashSet<>(mUserController.getRunningUsersLU()));
    }

    /** Test scheduling stopping of background users - reschedule if current user is a guest. */
    @Test
    public void testScheduleStopOfBackgroundUser_rescheduleWhenGuest() throws Exception {
        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);

        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
                /* backgroundUserScheduledStopTimeSecs= */ 2);

        final int TEST_USER_GUEST = 902;
        setUpUser(TEST_USER_GUEST, UserInfo.FLAG_GUEST);

        setUpUser(TEST_USER_ID2, NO_USERINFO_FLAGS);

        // Switch to TEST_USER_ID from user 0
        int numberOfUserSwitches = 0;
        addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
                ++numberOfUserSwitches, false,
                /* expectScheduleBackgroundUserStopping= */ false);
        assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID),
                mUserController.getRunningUsersLU());

        // Switch to TEST_USER_GUEST from TEST_USER_ID
        addForegroundUserAndContinueUserSwitch(TEST_USER_GUEST, TEST_USER_ID,
                ++numberOfUserSwitches, false,
                /* expectScheduleBackgroundUserStopping= */ true);
        assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_GUEST),
                mUserController.getRunningUsersLU());

        // Allow the post-switch processing to complete.
        // TEST_USER_ID may be scheduled for stopping, but it shouldn't actually stop since the
        // current user is a Guest.
        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
        assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_GUEST);
        assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_GUEST),
                mUserController.getRunningUsersLU());

        // Switch to TEST_USER_ID2 from TEST_USER_GUEST
        // Guests are automatically stopped in the background, so it won't be scheduled.
        addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_GUEST,
                ++numberOfUserSwitches, true,
                /* expectScheduleBackgroundUserStopping= */ false);
        assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_ID2),
                mUserController.getRunningUsersLU());

        // Allow the post-switch processing to complete.
        // TEST_USER_ID should *still* be scheduled for stopping, since we skipped stopping it
        // earlier.
        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
        assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_GUEST);
        assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_ID2);
        assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID2),
                mUserController.getRunningUsersLU());
    }

    /**
     * Process queued SCHEDULED_STOP_BACKGROUND_USER_MSG message, if expected.
     * @param userId the user we are checking to see whether it is scheduled.
@@ -682,11 +737,11 @@ public class UserControllerTest {
            boolean expectScheduled, @Nullable Integer userId) {
        TestHandler handler = mInjector.mHandler;
        if (expectScheduled) {
            assertTrue(handler.hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
            assertTrue(handler.hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
            handler.removeMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId);
            mUserController.processScheduledStopOfBackgroundUser(userId);
        } else {
            assertFalse(handler.hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
            assertFalse(handler.hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
        }
    }

@@ -1534,9 +1589,9 @@ public class UserControllerTest {
        mInjector.mHandler.clearAllRecordedMessages();
        // Verify that continueUserSwitch worked as expected
        continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
        assertEquals(mInjector.mHandler
                        .hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, expectedOldUserId),
                expectScheduleBackgroundUserStopping);
        assertEquals(expectScheduleBackgroundUserStopping,
                mInjector.mHandler
                        .hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, expectedOldUserId));
        verify(mInjector, times(expectedNumberOfCalls)).dismissUserSwitchingDialog(any());
        continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping,
                expectScheduleBackgroundUserStopping);
@@ -1810,6 +1865,13 @@ public class UserControllerTest {
    }

    private static class TestHandler extends Handler {
        /**
         * Keeps an accessible copy of messages that were queued for us to query.
         *
         * WARNING: queued messages get added to this, but processed/removed messages to NOT
         * automatically get removed. This can lead to confusing bugs. Maybe one day someone will
         * fix this, but in the meantime, this is your warning.
         */
        private final List<Message> mMessages = new ArrayList<>();

        TestHandler(Looper looper) {