summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/multiuser.aconfig7
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/UserController.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java208
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) {