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

Commit 7d1342e0 authored by Felipe Leme's avatar Felipe Leme Committed by Grace Cheng
Browse files

Moved user visibility callback to its own listener.

Commit 9edf758f introduced a
onUserVisibilityChanged() method on SystemService, but although
the user visibility change is typically associated with its
lifecycle state changes (which is what is reported by those
SystemService callback), that's not always the case. For example,
if the user that is switched to the foreground was already running
in the background, both that user visibility and the visibility of
its profiles would be changed to visible (if they were also in the
background), even though their "lifecycle state" didn't change.

So, it's better to handle these changes in a separate listener.

Test: adb logcat -D -b events | egrep '(I um_|I ssm_)' # while changing users
Test: atest FrameworksServicesTests:UserControllerTest

Bug: 244333150
Bug: 255895655

Change-Id: Ic5a51a4c75a0ec343be944f2628a3cd1f9e75898
parent 0fc931f2
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -472,18 +472,6 @@ public abstract class SystemService {
    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
    }

    /**
     * The {@link UserManager#isUserVisible() user visibility} changed.
     *
     * <p>This callback is called before the user starts or is switched to (or after it stops), when
     * its visibility changed because of that action.
     *
     * @hide
     */
    // NOTE: change visible to int if this method becomes a @SystemApi
    public void onUserVisibilityChanged(@NonNull TargetUser user, boolean visible) {
    }

    /**
     * Called when an existing user is stopping, for system services to finalize any per-user
     * state they maintain for running users.  This is called prior to sending the SHUTDOWN
+4 −58
Original line number Diff line number Diff line
@@ -82,10 +82,6 @@ public final class SystemServiceManager implements Dumpable {
    private static final String USER_STOPPING = "Stop"; // Logged as onUserStopping()
    private static final String USER_STOPPED = "Cleanup"; // Logged as onUserStopped()
    private static final String USER_COMPLETED_EVENT = "CompletedEvent"; // onUserCompletedEvent()
    private static final String USER_VISIBLE = "Visible"; // Logged on onUserVisible() and
                                                          // onUserStarting() (when visible is true)
    private static final String USER_INVISIBLE = "Invisible"; // Logged on onUserStopping()
                                                              // (when visibilityChanged is true)

    // The default number of threads to use if lifecycle thread pool is enabled.
    private static final int DEFAULT_MAX_USER_POOL_THREADS = 3;
@@ -354,57 +350,16 @@ public final class SystemServiceManager implements Dumpable {
    /**
     * Starts the given user.
     */
    public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId,
            boolean visible) {
        EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId, visible ? 1 : 0);
    public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
        EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId);

        final TargetUser targetUser = newTargetUser(userId);
        synchronized (mTargetUsers) {
            mTargetUsers.put(userId, targetUser);
        }

        if (visible) {
            // Must send the user visiiblity change first, for 2 reasons:
            // 1. Automotive need to update the user-zone mapping ASAP and it's one of the few
            // services listening to this event (OTOH, there  are manyy listeners to USER_STARTING
            // and some can take a while to process it)
            // 2. When a user is switched from bg to fg, the onUserVisibilityChanged() callback is
            // called onUserSwitching(), so calling it before onUserStarting() make it more
            // consistent with that
            EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, /* visible= */ 1);
            onUser(t, USER_VISIBLE, /* prevUser= */ null, targetUser);
        }
        onUser(t, USER_STARTING, /* prevUser= */ null, targetUser);
    }

    /**
     * Updates the user visibility.
     *
     * <p><b>NOTE: </b>this method should only be called when a user that is already running become
     * visible; if the user is starting visible, callers should call
     * {@link #onUserStarting(TimingsTraceAndSlog, int, boolean)} instead.
     */
    public void onUserVisible(@UserIdInt int userId) {
        EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, /* visible= */ 1);
        onUser(USER_VISIBLE, userId);
    }

    /**
     * Updates the visibility of the system user.
     *
     * <p>Since the system user never stops, this method must be called when it's switched from / to
     * foreground.
     */
    public void onSystemUserVisibilityChanged(boolean visible) {
        int userId = UserHandle.USER_SYSTEM;
        EventLog.writeEvent(EventLogTags.SSM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
        if (visible) {
            onUser(USER_VISIBLE, userId);
        } else {
            onUser(USER_INVISIBLE, userId);
        }
    }

    /**
     * Unlocks the given user.
     */
@@ -452,12 +407,9 @@ public final class SystemServiceManager implements Dumpable {
    /**
     * Stops the given user.
     */
    public void onUserStopping(@UserIdInt int userId, boolean visibilityChanged) {
        EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId, visibilityChanged ? 1 : 0);
    public void onUserStopping(@UserIdInt int userId) {
        EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId);
        onUser(USER_STOPPING, userId);
        if (visibilityChanged) {
            onUser(USER_INVISIBLE, userId);
        }
    }

    /**
@@ -580,12 +532,6 @@ public final class SystemServiceManager implements Dumpable {
                        threadPool.submit(getOnUserCompletedEventRunnable(
                                t, service, serviceName, curUser, completedEventType));
                        break;
                    case USER_VISIBLE:
                        service.onUserVisibilityChanged(curUser, /* visible= */ true);
                        break;
                    case USER_INVISIBLE:
                        service.onUserVisibilityChanged(curUser, /* visible= */ false);
                        break;
                    default:
                        throw new IllegalArgumentException(onWhat + " what?");
                }
+6 −6
Original line number Diff line number Diff line
@@ -107,21 +107,21 @@ option java_package com.android.server.am
30079 uc_dispatch_user_switch (oldUserId|1|5),(newUserId|1|5)
30080 uc_continue_user_switch (oldUserId|1|5),(newUserId|1|5)
30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)

# Tags below are used by SystemServiceManager - although it's technically part of am, these are
# also user switch events and useful to be analyzed together with events above.
30082 ssm_user_starting (userId|1|5),(visible|1)
30082 ssm_user_starting (userId|1|5)
30083 ssm_user_switching (oldUserId|1|5),(newUserId|1|5)
30084 ssm_user_unlocking (userId|1|5)
30085 ssm_user_unlocked (userId|1|5)
30086 ssm_user_stopping (userId|1|5),(visibilityChanged|1)
30086 ssm_user_stopping (userId|1|5)
30087 ssm_user_stopped (userId|1|5)
30088 ssm_user_completed_event (userId|1|5),(eventFlag|1|5)
30089 ssm_user_visibility_changed (userId|1|5),(visible|1)

# Similarly, tags below are used by UserManagerService
30091 um_user_visibility_changed (userId|1|5),(visible|1)

# Foreground service start/stop events.
30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)


+57 −27
Original line number Diff line number Diff line
@@ -176,9 +176,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 USER_VISIBLE_MSG = 150;

    private static final int NO_ARG2 = 0;
    static final int USER_VISIBILITY_CHANGED_MSG = 150;

    // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
    // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
@@ -430,10 +428,13 @@ class UserController implements Handler.Callback {
    /** @see #getLastUserUnlockingUptime */
    private volatile long mLastUserUnlockingUptime = 0;

    // TODO(b/244333150) remove this array and let UserVisibilityMediator call the listeners
    // directly, as that class should be responsible for all user visibility logic (for example,
    // when the foreground user is switched out, its profiles also become invisible)
    /**
     * List of visible users (as defined by {@link UserManager#isUserVisible()}).
     *
     * <p>It's only used to call {@link SystemServiceManager} when the visibility is changed upon
     * <p>It's only used to call {@link UserManagerInternal} when the visibility is changed upon
     * the user starting or stopping.
     *
     * <p>Note: only the key is used, not the value.
@@ -1087,10 +1088,7 @@ class UserController implements Handler.Callback {
            synchronized (mLock) {
                visibleBefore = mVisibleUsers.get(userId);
                if (visibleBefore) {
                    if (DEBUG_MU) {
                        Slogf.d(TAG, "Removing %d from mVisibleUsers", userId);
                    }
                    mVisibleUsers.delete(userId);
                    deleteVisibleUserLocked(userId);
                    visibilityChanged = true;
                } else {
                    visibilityChanged = false;
@@ -1139,6 +1137,20 @@ class UserController implements Handler.Callback {
        }
    }

    private void addVisibleUserLocked(@UserIdInt int userId) {
        if (DEBUG_MU) {
            Slogf.d(TAG, "adding %d to mVisibleUsers", userId);
        }
        mVisibleUsers.put(userId, true);
    }

    private void deleteVisibleUserLocked(@UserIdInt int userId) {
        if (DEBUG_MU) {
            Slogf.d(TAG, "deleting %d from mVisibleUsers", userId);
        }
        mVisibleUsers.delete(userId);
    }

    private void finishUserStopping(final int userId, final UserState uss,
            final boolean allowDelayedLocking, final boolean visibilityChanged) {
        EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
@@ -1157,7 +1169,10 @@ class UserController implements Handler.Callback {
        mInjector.batteryStatsServiceNoteEvent(
                BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
                Integer.toString(userId), userId);
        mInjector.getSystemServiceManager().onUserStopping(userId, visibilityChanged);
        mInjector.getSystemServiceManager().onUserStopping(userId);
        if (visibilityChanged) {
            mInjector.onUserVisibilityChanged(userId, /* visible= */ false);
        }

        Runnable finishUserStoppedAsync = () ->
                mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
@@ -1678,7 +1693,15 @@ class UserController implements Handler.Callback {
                // Make sure the old user is no longer considering the display to be on.
                mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
                boolean userSwitchUiEnabled;
                // TODO(b/244333150): temporary state until the callback logic is moved to
                // UserVisibilityManager
                int previousCurrentUserId; boolean notifyPreviousCurrentUserId;
                synchronized (mLock) {
                    previousCurrentUserId = mCurrentUserId;
                    notifyPreviousCurrentUserId = mVisibleUsers.get(previousCurrentUserId);
                    if (notifyPreviousCurrentUserId) {
                        deleteVisibleUserLocked(previousCurrentUserId);
                    }
                    mCurrentUserId = userId;
                    mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
                    userSwitchUiEnabled = mUserSwitchUiEnabled;
@@ -1696,6 +1719,11 @@ class UserController implements Handler.Callback {
                        mInjector.getWindowManager().lockNow(null);
                    }
                }
                if (notifyPreviousCurrentUserId) {
                    mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG,
                            previousCurrentUserId, 0));
                }

            } else {
                final Integer currentUserIdInt = mCurrentUserId;
                updateProfileRelatedCaches();
@@ -1721,10 +1749,7 @@ class UserController implements Handler.Callback {
                            && mInjector.getUserManagerInternal().isUserVisible(userId);
            if (visible) {
                synchronized (mLock) {
                    if (DEBUG_MU) {
                        Slogf.d(TAG, "Adding %d to mVisibleUsers", userId);
                    }
                    mVisibleUsers.put(userId, true);
                    addVisibleUserLocked(userId);
                }
            }

@@ -1767,12 +1792,15 @@ class UserController implements Handler.Callback {
                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId,
                        visible ? 1 : 0));
                t.traceEnd();
            } else if (visible) {
            }

            if (visible) {
                // User was already running and became visible (for example, when switching to a
                // user that was started in the background before), so it's necessary to explicitly
                // notify the services (while when the user starts from BOOTING, USER_START_MSG
                // takes care of that.
                mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBLE_MSG, userId, NO_ARG2));
                mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG, userId,
                        visible ? 1 : 0));
            }

            t.traceBegin("sendMessages");
@@ -2552,7 +2580,8 @@ class UserController implements Handler.Callback {
        if (!UserManager.isHeadlessSystemUserMode()) {
            // Don't need to call on HSUM because it will be called when the system user is
            // restarted on background
            mInjector.onUserStarting(UserHandle.USER_SYSTEM, /* visible= */ true);
            mInjector.onUserStarting(UserHandle.USER_SYSTEM);
            mInjector.onUserVisibilityChanged(UserHandle.USER_SYSTEM, /* visible= */ true);
        }
    }

@@ -2564,12 +2593,12 @@ class UserController implements Handler.Callback {
        int userId = UserHandle.USER_SYSTEM;
        synchronized (mLock) {
            if (visible) {
                mVisibleUsers.put(userId, true);
                addVisibleUserLocked(userId);
            } else {
                mVisibleUsers.delete(userId);
                deleteVisibleUserLocked(userId);
            }
        }
        mInjector.notifySystemUserVisibilityChanged(visible);
        mInjector.onUserVisibilityChanged(userId, visible);
        t.traceEnd();
    }

@@ -3069,7 +3098,7 @@ class UserController implements Handler.Callback {
                logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
                        USER_LIFECYCLE_EVENT_STATE_BEGIN);

                mInjector.onUserStarting(/* userId= */ msg.arg1, /* visible= */ msg.arg2 == 1);
                mInjector.onUserStarting(/* userId= */ msg.arg1);
                scheduleOnUserCompletedEvent(msg.arg1,
                        UserCompletedEventType.EVENT_TYPE_USER_STARTING,
                        USER_COMPLETED_EVENT_DELAY_MS);
@@ -3150,8 +3179,9 @@ class UserController implements Handler.Callback {
            case COMPLETE_USER_SWITCH_MSG:
                completeUserSwitch(msg.arg1);
                break;
            case USER_VISIBLE_MSG:
                mInjector.getSystemServiceManager().onUserVisible(/* userId= */ msg.arg1);
            case USER_VISIBILITY_CHANGED_MSG:
                mInjector.onUserVisibilityChanged(/* userId= */ msg.arg1,
                        /* visible= */ msg.arg2 == 1);
                break;
        }
        return false;
@@ -3675,12 +3705,12 @@ class UserController implements Handler.Callback {
            return UserManager.isUsersOnSecondaryDisplaysEnabled();
        }

        void onUserStarting(int userId, boolean visible) {
            getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId,
                    visible);
        void onUserStarting(@UserIdInt int userId) {
            getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
        }
        void notifySystemUserVisibilityChanged(boolean visible) {
            getSystemServiceManager().onSystemUserVisibilityChanged(visible);

        void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
            getUserManagerInternal().onUserVisibilityChanged(userId, visible);
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -140,7 +140,9 @@ import com.android.server.location.provider.StationaryThrottlingLocationProvider
import com.android.server.location.provider.proxy.ProxyLocationProvider;
import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.utils.Slogf;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -308,6 +310,10 @@ public class LocationManagerService extends ILocationManager.Stub implements
        permissionManagerInternal.setLocationExtraPackagesProvider(
                userId -> mContext.getResources().getStringArray(
                        com.android.internal.R.array.config_locationExtraPackageNames));

        // TODO(b/241604546): properly handle this callback
        LocalServices.getService(UserManagerInternal.class).addUserVisibilityListener(
                (u, v) -> Slogf.i(TAG, "onUserVisibilityChanged(): %d -> %b", u, v));
    }

    @Nullable
Loading