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

Commit 40bf00b2 authored by Adam Bookatz's avatar Adam Bookatz Committed by Android (Google) Code Review
Browse files

Merge "Reschedule bg stop if user has visible activities" into main

parents 89259af7 fec7fc4c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -298,6 +298,13 @@ flag {
    bug: "419867128"
}

flag {
    name: "reschedule_stop_if_visible_activities"
    namespace: "multiuser"
    description: "Don't automatically stop a background user that has a top visible activity; instead, reschedule it to stop later"
    bug: "423731424"
}

flag {
    name: "stop_previous_user_apps"
    namespace: "multiuser"
+44 −1
Original line number Diff line number Diff line
@@ -150,6 +150,7 @@ import com.android.server.pm.UserManagerInternal.UserStartMode;
import com.android.server.pm.UserManagerService;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityAssistInfo;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerService;

@@ -621,11 +622,12 @@ class UserController implements Handler.Callback {
        }

        final List<UserInfo> users = mInjector.getUserManager().getUsers(true);
        final ArraySet<Integer> visibleActivityUsers = mInjector.getVisibleActivityUsers();
        for (int i = 0; i < users.size(); i++) {
            final int userId = users.get(i).id;
            if (isUserVisible(userId)) {
                exemptedUsers.add(userId);
            } else if (avoidStoppingUserRightNow(userId)) {
            } else if (avoidStoppingUserRightNow(userId, visibleActivityUsers)) {
                avoidUsers.add(userId);
            }
        }
@@ -2846,6 +2848,16 @@ class UserController implements Handler.Callback {
     * Makes requests of other services, so don't call while holding a lock.
     */
    private boolean avoidStoppingUserRightNow(@UserIdInt int userId) {
        return avoidStoppingUserRightNow(userId, mInjector.getVisibleActivityUsers());
    }

    /**
     * Same as {@link #avoidStoppingUserRightNow(int)} but, for efficiency, takes the output of
     * {@link Injector#getVisibleActivityUsers()} as a parameter.
     */
    private boolean avoidStoppingUserRightNow(
            @UserIdInt int userId, ArraySet<Integer> visibleActivityUsers) {

        if (!android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
            return false;
        }
@@ -2866,6 +2878,12 @@ class UserController implements Handler.Callback {
                        userId, relatedUserId);
                return true;
            }
            if (visibleActivityUsers.contains(userId)) {
                // User is displaying the top activity from a currently visible root task.
                Slogf.d(TAG, "Avoid stopping user %d because user %d has a visible activity",
                        userId, relatedUserId);
                return true;
            }
        }
        return false;
    }
@@ -3599,6 +3617,9 @@ class UserController implements Handler.Callback {
    /**
     * Returns whether the user is currently visible, including users visible on a background
     * display and always-visible users (e.g. the communal profile).
     *
     * <p>Note that this is whether the user itself is considered a visible user, not merely a user
     * that happens to be displaying a {@link Injector#getVisibleActivityUsers() visible activity}.
     */
    private boolean isUserVisible(@UserIdInt int userId) {
        return mInjector.getUserManagerInternal().isUserVisible(userId);
@@ -4535,5 +4556,27 @@ class UserController implements Handler.Callback {
                t.traceEnd();
            }
        }

        /**
         * Returns the set of users that are currently displaying visible activities.
         *
         * <p>Even a non-visible background user could be visibly displaying an activity in special
         * circumstances, such as if it is running an app with
         * {@link android.content.pm.ActivityInfo#FLAG_SHOW_FOR_ALL_USERS}.
         */
        ArraySet<Integer> getVisibleActivityUsers() {
            if (!android.multiuser.Flags.rescheduleStopIfVisibleActivities()) {
                return new ArraySet<>();
            }
            ActivityTaskManagerInternal atmi
                    = LocalServices.getService(ActivityTaskManagerInternal.class);
            final ArraySet<Integer> visibleActivityUsers = new ArraySet<>();
            if (atmi != null) {
                for (ActivityAssistInfo info : atmi.getTopVisibleActivities()) {
                    visibleActivityUsers.add(info.getUserId());
                }
            }
            return visibleActivityUsers;
        }
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.util.Log;
import android.util.StringBuilderPrinter;
import android.util.TimeUtils;
@@ -1020,6 +1021,32 @@ public class UserControllerTest {
        assertRunningUsersIgnoreOrder(SYSTEM_USER_ID, TEST_USER_ID);
    }

    /** Test scheduling stopping of background users - reschedule if user has visible activity. */
    @Test
    public void testScheduleStopOfBackgroundUser_rescheduleIfVisibleActivity() throws Exception {
        mSetFlagsRule.enableFlags(
                android.multiuser.Flags.FLAG_RESCHEDULE_STOP_IF_VISIBLE_ACTIVITIES,
                android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER,
                android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER_BY_DEFAULT);
        assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());

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

        setUpAndStartUserInBackground(TEST_USER_ID);
        setUpAndStartUserInBackground(TEST_USER_ID1);
        assertRunningUsersIgnoreOrder(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_ID1);

        doReturn(new ArraySet(List.of(TEST_USER_ID))).when(mInjector).getVisibleActivityUsers();

        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID1);

        // TEST_USER_ID1 should be stopped. But TEST_USER_ID shouldn't as it has a visible activity.
        assertRunningUsersIgnoreOrder(SYSTEM_USER_ID, TEST_USER_ID);
    }

    /**
     * Process queued SCHEDULED_STOP_BACKGROUND_USER_MSG message, if expected.
     * @param userId the user we are checking to see whether it is scheduled.