diff options
6 files changed, 274 insertions, 39 deletions
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 4963a4f27803..321e539816b2 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -171,6 +171,13 @@ flag { } flag { + name: "schedule_stop_of_background_user" + namespace: "multiuser" + description: "Schedule background users to be stopped at a future point." + bug: "330351042" +} + +flag { name: "disable_private_space_items_on_home" namespace: "profile_experiences" description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a622d36edc6a..1a618700cd17 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3009,6 +3009,10 @@ <!-- Maximum number of users we allow to be running at a time --> <integer name="config_multiuserMaxRunningUsers">3</integer> + <!-- Number of seconds of uptime after a full user enters the background before we attempt to + stop it due to inactivity. Set to -1 to disable scheduling stopping background users. --> + <integer name="config_backgroundUserScheduledStopTimeSecs">1800</integer> <!-- 30 minutes --> + <!-- Whether to delay user data locking for background user. If false, user switched-out from user switching will still be in running state until config_multiuserMaxRunningUsers is reached. Once config_multiuserMaxRunningUsers is diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index eccd7bf5ccd4..ead582721826 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -488,6 +488,7 @@ <java-symbol type="integer" name="config_lockSoundVolumeDb" /> <java-symbol type="integer" name="config_multiuserMaximumUsers" /> <java-symbol type="integer" name="config_multiuserMaxRunningUsers" /> + <java-symbol type="integer" name="config_backgroundUserScheduledStopTimeSecs" /> <java-symbol type="bool" name="config_multiuserDelayUserDataLocking" /> <java-symbol type="bool" name="config_multiuserVisibleBackgroundUsers" /> <java-symbol type="bool" name="config_multiuserVisibleBackgroundUsersOnDefaultDisplay" /> diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2cd706747328..9ed6e7e13f28 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8946,8 +8946,10 @@ public class ActivityManagerService extends IActivityManager.Stub com.android.internal.R.integer.config_multiuserMaxRunningUsers); final boolean delayUserDataLocking = res.getBoolean( com.android.internal.R.bool.config_multiuserDelayUserDataLocking); + final int backgroundUserScheduledStopTimeSecs = res.getInteger( + com.android.internal.R.integer.config_backgroundUserScheduledStopTimeSecs); mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers, - delayUserDataLocking); + delayUserDataLocking, backgroundUserScheduledStopTimeSecs); } mAppErrors.loadAppsNotReportingCrashesFromConfig(res.getString( com.android.internal.R.string.config_appsNotReportingCrashes)); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index dd4cee47bee9..b7030765d053 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -59,6 +59,7 @@ import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOC import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKING_USER; import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE; +import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND_VISIBLE; import static com.android.server.pm.UserManagerInternal.USER_START_MODE_FOREGROUND; import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString; @@ -200,6 +201,7 @@ class UserController implements Handler.Callback { static final int START_USER_SWITCH_FG_MSG = 120; static final int COMPLETE_USER_SWITCH_MSG = 130; static final int USER_COMPLETED_EVENT_MSG = 140; + static final int SCHEDULED_STOP_BACKGROUND_USER_MSG = 150; private static final int NO_ARG2 = 0; @@ -251,6 +253,14 @@ class UserController implements Handler.Callback { @GuardedBy("mLock") private int mMaxRunningUsers; + /** + * Number of seconds of uptime after a full user enters the background before we attempt + * to stop it due to inactivity. Set to -1 to disable scheduling stopping background users. + * + * Typically set by config_backgroundUserScheduledStopTimeSecs. + */ + private int mBackgroundUserScheduledStopTimeSecs = -1; + // Lock for internal state. private final Object mLock = new Object(); @@ -453,11 +463,12 @@ class UserController implements Handler.Callback { } void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers, - boolean delayUserDataLocking) { + boolean delayUserDataLocking, int backgroundUserScheduledStopTimeSecs) { synchronized (mLock) { mUserSwitchUiEnabled = userSwitchUiEnabled; mMaxRunningUsers = maxRunningUsers; mDelayUserDataLocking = delayUserDataLocking; + mBackgroundUserScheduledStopTimeSecs = backgroundUserScheduledStopTimeSecs; mInitialized = true; } } @@ -1091,6 +1102,10 @@ class UserController implements Handler.Callback { final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) { Slogf.i(TAG, "stopSingleUserLU userId=" + userId); + if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) { + mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, + Integer.valueOf(userId)); + } final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started // If canDelayDataLockingForUser() is true and allowDelayedLocking is false, we need @@ -1879,6 +1894,10 @@ class UserController implements Handler.Callback { // No matter what, the fact that we're requested to start the user (even if it is // already running) puts it towards the end of the mUserLru list. addUserToUserLru(userId); + if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) { + mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, + Integer.valueOf(userId)); + } if (unlockListener != null) { uss.mUnlockProgress.addListener(unlockListener); @@ -1923,6 +1942,9 @@ class UserController implements Handler.Callback { // of mUserLru, so we need to ensure that the foreground user isn't displaced. addUserToUserLru(mCurrentUserId); } + if (userStartMode == USER_START_MODE_BACKGROUND && !userInfo.isProfile()) { + scheduleStopOfBackgroundUser(userId); + } t.traceEnd(); // Make sure user is in the started state. If it is currently @@ -2294,6 +2316,65 @@ class UserController implements Handler.Callback { } } + /** + * Possibly schedules the user to be stopped at a future point. To be used to stop background + * users that haven't been actively used in a long time. + * This is only intended for full users that are currently in the background. + */ + private void scheduleStopOfBackgroundUser(@UserIdInt int oldUserId) { + if (!android.multiuser.Flags.scheduleStopOfBackgroundUser()) { + return; + } + final int delayUptimeSecs = mBackgroundUserScheduledStopTimeSecs; + if (delayUptimeSecs <= 0 || UserManager.isVisibleBackgroundUsersEnabled()) { + // Feature is not enabled on this device. + return; + } + if (oldUserId == UserHandle.USER_SYSTEM) { + // Never stop system user + 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 " + + "of it being the main user", oldUserId); + return; + } + Slogf.d(TAG, "Scheduling to stop user %d in %d seconds", oldUserId, delayUptimeSecs); + final int delayUptimeMs = delayUptimeSecs * 1000; + final Object msgObj = oldUserId; + mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, msgObj); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(SCHEDULED_STOP_BACKGROUND_USER_MSG, msgObj), + delayUptimeMs); + } + + /** + * Possibly stops the given full user due to it having been in the background for a long time. + * There is no guarantee of stopping the user; it is done discretionarily. + * + * This should never be called for background visible users; devices that support this should + * not use {@link #scheduleStopOfBackgroundUser(int)}. + * + * @param userIdInteger a full user to be stopped if it is still in the background + */ + @VisibleForTesting + void processScheduledStopOfBackgroundUser(Integer userIdInteger) { + final int userId = userIdInteger; + Slogf.d(TAG, "Considering stopping background user %d due to inactivity", userId); + synchronized (mLock) { + if (getCurrentOrTargetUserIdLU() == userId) { + return; + } + if (mPendingTargetUserIds.contains(userIdInteger)) { + // We'll soon want to switch to this user, so don't kill it now. + return; + } + Slogf.i(TAG, "Stopping background user %d due to inactivity", userId); + stopUsersLU(userId, /* allowDelayedLocking= */ true, null, null); + } + } + private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG); t.traceBegin("timeoutUserSwitch-" + oldUserId + "-to-" + newUserId); @@ -2428,6 +2509,7 @@ class UserController implements Handler.Callback { uss.switching = false; stopGuestOrEphemeralUserIfBackground(oldUserId); stopUserOnSwitchIfEnforced(oldUserId); + scheduleStopOfBackgroundUser(oldUserId); t.traceEnd(); // end continueUserSwitch } @@ -3309,6 +3391,8 @@ class UserController implements Handler.Callback { pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch()); pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch); pw.println(" mMaxRunningUsers:" + mMaxRunningUsers); + pw.println(" mBackgroundUserScheduledStopTimeSecs:" + + mBackgroundUserScheduledStopTimeSecs); pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled); pw.println(" mInitialized:" + mInitialized); pw.println(" mIsBroadcastSentForSystemUserStarted:" @@ -3435,6 +3519,9 @@ class UserController implements Handler.Callback { case COMPLETE_USER_SWITCH_MSG: completeUserSwitch(msg.arg1, msg.arg2); break; + case SCHEDULED_STOP_BACKGROUND_USER_MSG: + processScheduledStopOfBackgroundUser((Integer) msg.obj); + break; } return false; } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 7c0dbf4889da..c6f3eb357442 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -34,6 +34,7 @@ import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG; +import static com.android.server.am.UserController.SCHEDULED_STOP_BACKGROUND_USER_MSG; import static com.android.server.am.UserController.USER_COMPLETED_EVENT_MSG; import static com.android.server.am.UserController.USER_CURRENT_MSG; import static com.android.server.am.UserController.USER_START_MSG; @@ -323,7 +324,8 @@ public class UserControllerTest { @Test public void testStartUserUIDisabled() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND); verify(mInjector, never()).showUserSwitchingDialog( @@ -393,7 +395,8 @@ public class UserControllerTest { @Test public void testFailedStartUserInForeground() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mUserController.startUserInForeground(NONEXIST_USER_ID); verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); @@ -470,7 +473,8 @@ public class UserControllerTest { @Test public void testContinueUserSwitch() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); @@ -483,7 +487,7 @@ public class UserControllerTest { continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(0)).dismissKeyguard(any()); verify(mInjector, times(1)).dismissUserSwitchingDialog(any()); - continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false, false); verifySystemUserVisibilityChangesNeverNotified(); } @@ -491,7 +495,8 @@ public class UserControllerTest { public void testContinueUserSwitchDismissKeyguard() { when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false); mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); @@ -504,14 +509,15 @@ public class UserControllerTest { continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(1)).dismissKeyguard(any()); verify(mInjector, times(1)).dismissUserSwitchingDialog(any()); - continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false, false); verifySystemUserVisibilityChangesNeverNotified(); } @Test public void testContinueUserSwitchUIDisabled() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND); @@ -524,11 +530,11 @@ public class UserControllerTest { // Verify that continueUserSwitch worked as expected continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, never()).dismissUserSwitchingDialog(any()); - continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false, false); } private void continueUserSwitchAssertions(int expectedOldUserId, int expectedNewUserId, - boolean backgroundUserStopping) { + boolean backgroundUserStopping, boolean expectScheduleBackgroundUserStopping) { Set<Integer> expectedCodes = new LinkedHashSet<>(); expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); @@ -536,6 +542,9 @@ public class UserControllerTest { expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG); expectedCodes.add(0); // this is for directly posting in stopping. } + if (expectScheduleBackgroundUserStopping) { + expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG); + } Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes(); assertEquals("Unexpected message sent", expectedCodes, actualCodes); Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG); @@ -571,6 +580,112 @@ public class UserControllerTest { ).collect(Collectors.toList()), Collections.emptySet()); } + /** Test scheduling stopping of background users after a user-switch. */ + @Test + public void testScheduleStopOfBackgroundUser_switch() { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER); + + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ 2); + + setUpUser(TEST_USER_ID1, NO_USERINFO_FLAGS); + + // Switch to TEST_USER_ID from user 0 + int numberOfUserSwitches = 0; + addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM, + ++numberOfUserSwitches, + /* expectOldUserStopping= */false, + /* expectScheduleBackgroundUserStopping= */ false); + assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID), + mUserController.getRunningUsersLU()); + + // Allow the post-switch processing to complete (there should be no scheduled stopping). + assertAndProcessScheduledStopBackgroundUser(false, null); + assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID), + mUserController.getRunningUsersLU()); + + // Switch to TEST_USER_ID1 from TEST_USER_ID + addForegroundUserAndContinueUserSwitch(TEST_USER_ID1, TEST_USER_ID, + ++numberOfUserSwitches, + /* expectOldUserStopping= */false, + /* expectScheduleBackgroundUserStopping= */ true); + assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_ID1), + mUserController.getRunningUsersLU()); + + // Switch back to TEST_USER_ID from TEST_USER_ID1 + addForegroundUserAndContinueUserSwitch(TEST_USER_ID, TEST_USER_ID1, + ++numberOfUserSwitches, + /* expectOldUserStopping= */false, + /* expectScheduleBackgroundUserStopping= */ true); + assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID1, TEST_USER_ID), + mUserController.getRunningUsersLU()); + + // Allow the post-switch processing to complete. + assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_ID); + assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID1); + assertAndProcessScheduledStopBackgroundUser(false, null); + assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID), + mUserController.getRunningUsersLU()); + } + + /** Test scheduling stopping of background users that were started in the background. */ + @Test + public void testScheduleStopOfBackgroundUser_startInBackground() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER); + + mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, + /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ 2); + + // Start two full background users (which should both get scheduled for stopping) + // and one profile (which should not). + setUpAndStartUserInBackground(TEST_USER_ID); + setUpAndStartUserInBackground(TEST_USER_ID1); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_MANAGED); + + assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_ID1, TEST_USER_ID2), + new HashSet<>(mUserController.getRunningUsersLU())); + + assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID); + assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID1, TEST_USER_ID2), + new HashSet<>(mUserController.getRunningUsersLU())); + + assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID1); + assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID2), + new HashSet<>(mUserController.getRunningUsersLU())); + + assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_ID2); + assertAndProcessScheduledStopBackgroundUser(false, null); + + // Now that we've processed the stops, let's make sure that a subsequent one will work too. + setUpAndStartUserInBackground(TEST_USER_ID3); + assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID2, TEST_USER_ID3), + new HashSet<>(mUserController.getRunningUsersLU())); + assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID3); + assertAndProcessScheduledStopBackgroundUser(false, null); + assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID2), + new HashSet<>(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. + * Can be null, when expectScheduled is false, to indicate no user should be + * scheduled. + */ + private void assertAndProcessScheduledStopBackgroundUser( + boolean expectScheduled, @Nullable Integer userId) { + TestHandler handler = mInjector.mHandler; + if (expectScheduled) { + assertTrue(handler.hasMessages(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)); + } + } + @Test public void testExplicitSystemUserStartInBackground() { setUpUser(UserHandle.USER_SYSTEM, 0); @@ -587,13 +702,14 @@ public class UserControllerTest { public void testUserLockingFromUserSwitchingForMultipleUsersNonDelayedLocking() throws InterruptedException, RemoteException { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); setUpUser(TEST_USER_ID1, 0); setUpUser(TEST_USER_ID2, 0); int numberOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); // running: user 0, USER_ID assertTrue(mUserController.canStartMoreUsers()); assertEquals(Arrays.asList(new Integer[] {0, TEST_USER_ID}), @@ -601,7 +717,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(TEST_USER_ID1, TEST_USER_ID, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); // running: user 0, USER_ID, USER_ID1 assertFalse(mUserController.canStartMoreUsers()); assertEquals(Arrays.asList(new Integer[] {0, TEST_USER_ID, TEST_USER_ID1}), @@ -609,7 +725,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_ID1, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); UserState ussUser2 = mUserStates.get(TEST_USER_ID2); // skip middle step and call this directly. mUserController.finishUserSwitch(ussUser2); @@ -631,13 +747,14 @@ public class UserControllerTest { public void testUserLockingFromUserSwitchingForMultipleUsersDelayedLockingMode() throws Exception { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true, + /* backgroundUserScheduledStopTimeSecs= */ -1); setUpUser(TEST_USER_ID1, 0); setUpUser(TEST_USER_ID2, 0); int numberOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); // running: user 0, USER_ID assertTrue(mUserController.canStartMoreUsers()); assertEquals(Arrays.asList(new Integer[] {0, TEST_USER_ID}), @@ -645,7 +762,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(TEST_USER_ID1, TEST_USER_ID, - numberOfUserSwitches, true); + numberOfUserSwitches, true, false); // running: user 0, USER_ID1 // stopped + unlocked: USER_ID numberOfUserSwitches++; @@ -663,7 +780,7 @@ public class UserControllerTest { .lockCeStorage(anyInt()); addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_ID1, - numberOfUserSwitches, true); + numberOfUserSwitches, true, false); // running: user 0, USER_ID2 // stopped + unlocked: USER_ID1 // stopped + locked: USER_ID @@ -686,7 +803,8 @@ public class UserControllerTest { public void testStoppingExcessRunningUsersAfterSwitch_currentProfileNotStopped() throws Exception { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 5, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 5, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); final int PARENT_ID = 200; final int PROFILE1_ID = 201; @@ -707,7 +825,7 @@ public class UserControllerTest { int numberOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(PARENT_ID, UserHandle.USER_SYSTEM, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); mUserController.finishUserSwitch(mUserStates.get(PARENT_ID)); waitForHandlerToComplete(mInjector.mHandler, HANDLER_WAIT_TIME_MS); assertTrue(mUserController.canStartMoreUsers()); @@ -722,7 +840,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(FG_USER_ID, PARENT_ID, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); mUserController.finishUserSwitch(mUserStates.get(FG_USER_ID)); waitForHandlerToComplete(mInjector.mHandler, HANDLER_WAIT_TIME_MS); assertTrue(mUserController.canStartMoreUsers()); @@ -747,7 +865,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(PARENT_ID, FG_USER_ID, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); mUserController.finishUserSwitch(mUserStates.get(PARENT_ID)); waitForHandlerToComplete(mInjector.mHandler, HANDLER_WAIT_TIME_MS); // We've now done a user switch and should notice that we've exceeded the maximum number of @@ -766,7 +884,8 @@ public class UserControllerTest { @Test public void testRunningUsersListOrder_parentAfterProfile() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 7, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 7, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); final int PARENT_ID = 200; final int PROFILE1_ID = 201; @@ -787,7 +906,7 @@ public class UserControllerTest { int numberOfUserSwitches = 1; addForegroundUserAndContinueUserSwitch(PARENT_ID, UserHandle.USER_SYSTEM, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); assertEquals(Arrays.asList( new Integer[] {SYSTEM_USER_ID, PARENT_ID}), mUserController.getRunningUsersLU()); @@ -799,7 +918,7 @@ public class UserControllerTest { numberOfUserSwitches++; addForegroundUserAndContinueUserSwitch(FG_USER_ID, PARENT_ID, - numberOfUserSwitches, false); + numberOfUserSwitches, false, false); assertEquals(Arrays.asList( new Integer[] {SYSTEM_USER_ID, PROFILE1_ID, PARENT_ID, FG_USER_ID}), mUserController.getRunningUsersLU()); @@ -827,7 +946,8 @@ public class UserControllerTest { @Test public void testRunningUsersListOrder_currentAtEnd() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 7, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 7, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); final int CURRENT_ID = 200; final int PROFILE_ID = 201; @@ -842,7 +962,7 @@ public class UserControllerTest { new Integer[] {SYSTEM_USER_ID}), mUserController.getRunningUsersLU()); - addForegroundUserAndContinueUserSwitch(CURRENT_ID, UserHandle.USER_SYSTEM, 1, false); + addForegroundUserAndContinueUserSwitch(CURRENT_ID, UserHandle.USER_SYSTEM, 1, false, false); assertEquals(Arrays.asList( new Integer[] {SYSTEM_USER_ID, CURRENT_ID}), mUserController.getRunningUsersLU()); @@ -864,7 +984,8 @@ public class UserControllerTest { @Test public void testUserLockingWithStopUserForNonDelayedLockingMode() throws Exception { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); setUpAndStartUserInBackground(TEST_USER_ID); assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* allowDelayedLocking= */ true, @@ -922,7 +1043,8 @@ public class UserControllerTest { @Test public void testUserLockingForDelayedLockingMode() throws Exception { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true, + /* backgroundUserScheduledStopTimeSecs= */ -1); // allowDelayedLocking set and no KeyEvictedCallback, so it should not lock. setUpAndStartUserInBackground(TEST_USER_ID); @@ -973,7 +1095,8 @@ public class UserControllerTest { @Test public void testStopProfile_doesNotStopItsParent() throws Exception { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 5, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 5, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); final Range<Integer> RUNNING_RANGE = Range.closed(UserState.STATE_BOOTING, UserState.STATE_RUNNING_UNLOCKED); @@ -1053,7 +1176,8 @@ public class UserControllerTest { @Test public void testStopPrivateProfile() throws Exception { mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); @@ -1071,7 +1195,8 @@ public class UserControllerTest { @Test public void testStopPrivateProfileWithDelayedLocking() throws Exception { mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); @@ -1083,7 +1208,8 @@ public class UserControllerTest { @Test public void testStopPrivateProfileWithDelayedLocking_flagDisabled() throws Exception { mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); mSetFlagsRule.disableFlags( @@ -1113,7 +1239,8 @@ public class UserControllerTest { public void testStopPrivateProfileWithDelayedLocking_imperviousToNumberOfRunningUsers() throws Exception { mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, - /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); @@ -1130,7 +1257,8 @@ public class UserControllerTest { @Test public void testStopManagedProfileWithDelayedLocking() throws Exception { mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); @@ -1285,7 +1413,8 @@ public class UserControllerTest { public void testStallUserSwitchUntilTheKeyguardIsShown() throws Exception { // enable user switch ui, because keyguard is only shown then mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, - /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false, + /* backgroundUserScheduledStopTimeSecs= */ -1); // mock the device to be secure in order to expect the keyguard to be shown when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); @@ -1365,7 +1494,8 @@ public class UserControllerTest { } private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId, - int expectedNumberOfCalls, boolean expectOldUserStopping) { + int expectedNumberOfCalls, boolean expectOldUserStopping, + boolean expectScheduleBackgroundUserStopping) { // Start user -- this will update state of mUserController mUserController.startUser(newUserId, USER_START_MODE_FOREGROUND); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); @@ -1378,8 +1508,12 @@ 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); verify(mInjector, times(expectedNumberOfCalls)).dismissUserSwitchingDialog(any()); - continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping); + continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping, + expectScheduleBackgroundUserStopping); } private UserInfo setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) { |