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

Commit 0d9fb182 authored by Adam Bookatz's avatar Adam Bookatz
Browse files

Don't exempt MainUser in startUserInBackgroundTemp

startUserInBackgroundTemporarily() uses the same infrastructure as
config_backgroundUserScheduledStopTimeSecs. However, for the latter we
want to exempt the MainUser from being automatically stopped, whereas we
have no such desire for the former (since it is being stopped by
request, rather than just due to inactivity). We therefore slightly
separate the two, having the MainUser exemption only apply to the
latter.

For simplicity, we also make it so that requested duration in
startUserInBackgroundTemporarily() replaces that of
config_backgroundUserScheduledStopTimeSecs when this method is called.
We previously took the smallest value between them, but that seems
overly complicated now. It isn't even clear what the best policy is, so
we may as well adopt the simpler one for now.

Bug: 401340391
Bug: 330351042
Test: UserControllerTest
Flag: android.multiuser.schedule_stop_of_background_user
Change-Id: I0f7b2fdd2a13219e2c392276929f427a3dca8a0f
parent 4cb20f20
Loading
Loading
Loading
Loading
+9 −5
Original line number Diff line number Diff line
@@ -166,12 +166,16 @@ public abstract class ActivityManagerInternal {
     * Start user in the background but only temporarily; if the user hasn't left the background
     * in the provided duration, it may be automatically stopped (at the system's discretion).
     *
     * The automatic stopping is not guaranteed, and there are many cases in which it won't be.
     * The automatic stopping is not guaranteed, and there are cases in which it won't be.
     * Similarly, there is no guarantee that the user will not be stopped prior to the given
     * duration.
     *
     * Conversely, there is no guarantee that the user will not be stopped prior to the given
     * duration; e.g. if the default inactive stopping time (from
     * config_backgroundUserScheduledStopTimeSecs) is sooner, then we may stop it then
     * instead. There is no guarantee about this implementation.
     * In the current implementation, this value simply replaces any default inactive stopping time
     * (from config_backgroundUserScheduledStopTimeSecs), but that is subject to change.
     *
     * Automatically stopping background users is not currently enabled for devices supporting
     * {@link android.os.UserManager#isVisibleBackgroundUsersEnabled() visible background users};
     * on such devices, the user will still be started but not stopped.
     *
     * @param userId ID of the user to start
     * @param durSecs in how many seconds we should attempt to stop the user
+35 −35
Original line number Diff line number Diff line
@@ -2244,14 +2244,16 @@ class UserController implements Handler.Callback {

        if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
            if (userStartMode == USER_START_MODE_BACKGROUND && userInfo.isFull() &&
                    (autoStopUserInSecs = determineWhenToScheduleStop(autoStopUserInSecs)) > 0) {
                    (autoStopUserInSecs > 0 || mBackgroundUserScheduledStopTimeSecs > 0)) {
                if (!needStart
                        && !mHandler.hasEqualMessages(SCHEDULE_STOP_BACKGROUND_USER_MSG,
                                Integer.valueOf(userId))) {
                    Slogf.d(TAG, "Not scheduling background user stop: user %d is already running"
                            + " in background in perpetuity, so keep it that way", userId);
                } else {
                } else if (autoStopUserInSecs > 0) {
                    scheduleStopOfBackgroundUser(userId, autoStopUserInSecs);
                } else {
                    scheduleStopOfInactiveBackgroundUser(userId);
                }
            } else {
                // This start shouldn't be scheduled for stopping. Clear existing scheduled stops.
@@ -2306,22 +2308,6 @@ class UserController implements Handler.Callback {
        }
    }

    /**
     * Determines when to schedule stopping a background user, balancing between the provided
     * parameter and the default {@link #mBackgroundUserScheduledStopTimeSecs}. It is the smallest
     * positive value among them.
     * Returns a non-positive value (i.e. don't schedule) if both are non-positive.
     */
    private int determineWhenToScheduleStop(int autoStopUserInSecs) {
        if (autoStopUserInSecs <= 0) {
            return mBackgroundUserScheduledStopTimeSecs;
        }
        if (mBackgroundUserScheduledStopTimeSecs <= 0) {
            return autoStopUserInSecs;
        }
        return Math.min(mBackgroundUserScheduledStopTimeSecs, autoStopUserInSecs);
    }

    /**
     * Start user, if it's not already running, and bring it to foreground.
     */
@@ -2652,8 +2638,23 @@ class UserController implements Handler.Callback {
     * users that haven't been actively used in a long time, using the default delay for that.
     * This is only intended for full users that are currently in the background.
     */
    private void scheduleStopOfInactiveBackgroundUser(@UserIdInt int oldUserId) {
        scheduleStopOfBackgroundUser(oldUserId, mBackgroundUserScheduledStopTimeSecs);
    private void scheduleStopOfInactiveBackgroundUser(@UserIdInt int userId) {
        if (!android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
            return;
        }
        if (mBackgroundUserScheduledStopTimeSecs <= 0) {
            // Feature is not enabled on this device.
            return;
        }
        if (userId == 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", userId);
            mHandler.removeEqualMessages(SCHEDULE_STOP_BACKGROUND_USER_MSG,
                    Integer.valueOf(userId));
            return;
        }
        scheduleStopOfBackgroundUser(userId, mBackgroundUserScheduledStopTimeSecs);
    }

    /**
@@ -2662,43 +2663,40 @@ class UserController implements Handler.Callback {
     * some reason, and therefore are rescheduling it until a later point.
     * This is only intended for full users that are currently in the background.
     */
    private void rescheduleStopOfBackgroundUser(@UserIdInt int oldUserId) {
        scheduleStopOfBackgroundUser(oldUserId, POSTPONEMENT_TIME_FOR_BACKGROUND_USER_STOP_SECS);
    private void rescheduleStopOfBackgroundUser(@UserIdInt int userId) {
        scheduleStopOfBackgroundUser(userId, POSTPONEMENT_TIME_FOR_BACKGROUND_USER_STOP_SECS);
    }

    /**
     * Possibly schedules the user to be stopped at after the given number of seconds.
     * Possibly schedules the user to be stopped after the given number of seconds.
     * This is only intended for full users that are currently in the background.
     */
    private void scheduleStopOfBackgroundUser(@UserIdInt int oldUserId, int delayUptimeSecs) {
    private void scheduleStopOfBackgroundUser(@UserIdInt int userId, int delayUptimeSecs) {
        if (!android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
            return;
        }
        if (delayUptimeSecs <= 0 || UserManager.isVisibleBackgroundUsersEnabled()) {
        if (delayUptimeSecs <= 0) {
            return;
        }
        if (UserManager.isVisibleBackgroundUsersEnabled()) {
            // Feature is not enabled on this device.
            return;
        }
        if (oldUserId == UserHandle.USER_SYSTEM) {
        if (userId == UserHandle.USER_SYSTEM) {
            // Never stop system user
            return;
        }
        synchronized(mLock) {
            final UserState uss = mStartedUsers.get(oldUserId);
            final UserState uss = mStartedUsers.get(userId);
            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 "
                    + "of it being the main user", oldUserId);
            return;
        }
        Slogf.d(TAG, "Scheduling to stop user %d in %d seconds", oldUserId, delayUptimeSecs);
        Slogf.d(TAG, "Scheduling to stop user %d in %d seconds", userId, delayUptimeSecs);
        final int delayUptimeMs = delayUptimeSecs * 1000;
        final Object msgObj = oldUserId;
        final Object msgObj = userId;
        mHandler.sendMessageDelayed(
                mHandler.obtainMessage(SCHEDULE_STOP_BACKGROUND_USER_MSG, msgObj),
                delayUptimeMs);
@@ -2746,6 +2744,8 @@ class UserController implements Handler.Callback {
            final UserInfo currentOrTargetUser = getCurrentUserLU();
            if (currentOrTargetUser != null && currentOrTargetUser.isGuest()) {
                // Don't kill any background users for the sake of a Guest. Just reschedule instead.
                Slogf.d(TAG, "Current user %d is a Guest, so reschedule bg stopping of user %d",
                        currentOrTargetUser.id, userId);
                rescheduleStopOfBackgroundUser(userId);
                return;
            }