Loading services/core/java/com/android/server/am/UserController.java +14 −0 Original line number Diff line number Diff line Loading @@ -2335,6 +2335,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 " Loading Loading @@ -2371,6 +2379,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); } Loading services/core/java/com/android/server/pm/UserManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -5918,6 +5918,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) { Loading services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +67 −5 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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)); } } Loading Loading @@ -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); Loading Loading @@ -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) { Loading Loading
services/core/java/com/android/server/am/UserController.java +14 −0 Original line number Diff line number Diff line Loading @@ -2335,6 +2335,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 " Loading Loading @@ -2371,6 +2379,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); } Loading
services/core/java/com/android/server/pm/UserManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -5918,6 +5918,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) { Loading
services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +67 −5 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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)); } } Loading Loading @@ -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); Loading Loading @@ -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) { Loading