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

Commit e4a65094 authored by Felipe Leme's avatar Felipe Leme
Browse files

Delegated user visibility changes from UserController to UserVisibilityMediator.

Fixes: 244333150

Test: atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest UserVisibilityMediatorMUMDTest UserVisibilityMediatorSUSDTest UserControllerTest # unit
Test: atest MultipleUsersOnMultipleDisplaysTest CtsMultiUserTestCases:android.multiuser.cts.UserManagerTest # CTS
Test: adb logcat -D -b events | egrep 'I um_'

Change-Id: If5b5519f0a9cd32a0f58447eda8b8a3d168489bb
parent c7199796
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -977,12 +977,11 @@ message UserControllerProto {
        optional int32 profile = 2;
    }
    repeated UserProfile user_profile_group_ids = 4;
    repeated int32 visible_users_array = 5;

    // current_user contains the id of the current user, while current_profiles contains the ids of
    // both the current user and its profiles (if any)
    optional int32 current_user = 6;
    repeated int32 current_profiles = 7;
    optional int32 current_user = 5;
    repeated int32 current_profiles = 6;
}

// sync with com.android.server.am.AppTimeTracker.java
+13 −127
Original line number Diff line number Diff line
@@ -100,7 +100,6 @@ import android.util.EventLog;
import android.util.IntArray;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -177,7 +176,8 @@ 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_VISIBILITY_CHANGED_MSG = 150;

    private static final int NO_ARG2 = 0;

    // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
    // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
@@ -437,20 +437,6 @@ 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 UserManagerInternal} when the visibility is changed upon
     * the user starting or stopping.
     *
     * <p>Note: only the key is used, not the value.
     */
    @GuardedBy("mLock")
    private final SparseBooleanArray mVisibleUsers = new SparseBooleanArray();

    private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
        @Override
        public void onUserCreated(UserInfo user, Object token) {
@@ -1092,24 +1078,11 @@ class UserController implements Handler.Callback {
            // instead.
            userManagerInternal.unassignUserFromDisplayOnStop(userId);

            final boolean visibilityChanged;
            boolean visibleBefore;
            synchronized (mLock) {
                visibleBefore = mVisibleUsers.get(userId);
                if (visibleBefore) {
                    deleteVisibleUserLocked(userId);
                    visibilityChanged = true;
                } else {
                    visibilityChanged = false;
                }
            }

            updateStartedUserArrayLU();

            final boolean allowDelayedLockingCopied = allowDelayedLocking;
            Runnable finishUserStoppingAsync = () ->
                    mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied,
                            visibilityChanged));
                    mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));

            if (mInjector.getUserManager().isPreCreated(userId)) {
                finishUserStoppingAsync.run();
@@ -1146,22 +1119,8 @@ 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) {
            final boolean allowDelayedLocking) {
        EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
        synchronized (mLock) {
            if (uss.state != UserState.STATE_STOPPING) {
@@ -1179,9 +1138,6 @@ class UserController implements Handler.Callback {
                BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
                Integer.toString(userId), userId);
        mInjector.getSystemServiceManager().onUserStopping(userId);
        if (visibilityChanged) {
            mInjector.onUserVisibilityChanged(userId, /* visible= */ false);
        }

        Runnable finishUserStoppedAsync = () ->
                mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
@@ -1655,25 +1611,13 @@ class UserController implements Handler.Callback {
                    userInfo.profileGroupId, foreground, displayId);
            t.traceEnd();

            boolean visible;
            switch (result) {
                case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE:
                    visible = true;
                    break;
                case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE:
                    visible = false;
                    break;
                default:
                    Slogf.wtf(TAG, "Wrong result from assignUserToDisplayOnStart(): %d", result);
                    // Fall through
                case UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE:
            if (result == UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE) {
                Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s",
                        (foreground ? "fg" : "bg"), userId, displayId,
                        UserManagerInternal.userAssignmentResultToString(result));
                return false;
            }


            // TODO(b/239982558): might need something similar for bg users on secondary display
            if (foreground && isUserSwitchUiEnabled()) {
                t.traceBegin("startFreezingScreen");
@@ -1724,15 +1668,7 @@ 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;
@@ -1753,10 +1689,6 @@ 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;
@@ -1768,12 +1700,6 @@ class UserController implements Handler.Callback {
            }
            t.traceEnd();

            if (visible) {
                synchronized (mLock) {
                    addVisibleUserLocked(userId);
                }
            }

            // Make sure user is in the started state.  If it is currently
            // stopping, we need to knock that off.
            if (uss.state == UserState.STATE_STOPPING) {
@@ -1810,20 +1736,10 @@ class UserController implements Handler.Callback {
                // Booting up a new user, need to tell system services about it.
                // Note that this is on the same handler as scheduling of broadcasts,
                // which is important because it needs to go first.
                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId,
                        visible ? 1 : 0));
                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, NO_ARG2));
                t.traceEnd();
            }

            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_VISIBILITY_CHANGED_MSG, userId, 1));
            }

            t.traceBegin("sendMessages");
            if (foreground) {
                mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
@@ -2248,11 +2164,6 @@ class UserController implements Handler.Callback {
        uss.switching = false;
        stopGuestOrEphemeralUserIfBackground(oldUserId);
        stopUserOnSwitchIfEnforced(oldUserId);
        if (oldUserId == UserHandle.USER_SYSTEM) {
            // System user is never stopped, but its visibility is changed (as it is brought to the
            // background)
            updateSystemUserVisibility(t, /* visible= */ false);
        }

        t.traceEnd(); // end continueUserSwitch
    }
@@ -2614,27 +2525,10 @@ class UserController implements Handler.Callback {
            // 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);
            mInjector.onUserVisibilityChanged(UserHandle.USER_SYSTEM, /* visible= */ true);
            mInjector.onSystemUserVisibilityChanged(/* visible= */ true);
        }
    }

    private void updateSystemUserVisibility(TimingsTraceAndSlog t, boolean visible) {
        t.traceBegin("update-system-userVisibility-" + visible);
        if (DEBUG_MU) {
            Slogf.d(TAG, "updateSystemUserVisibility(): visible=%b", visible);
        }
        int userId = UserHandle.USER_SYSTEM;
        synchronized (mLock) {
            if (visible) {
                addVisibleUserLocked(userId);
            } else {
                deleteVisibleUserLocked(userId);
            }
        }
        mInjector.onUserVisibilityChanged(userId, visible);
        t.traceEnd();
    }

    /**
     * Refreshes the internal caches related to user profiles.
     *
@@ -3032,9 +2926,6 @@ class UserController implements Handler.Callback {
                    proto.end(uToken);
                }
            }
            for (int i = 0; i < mVisibleUsers.size(); i++) {
                proto.write(UserControllerProto.VISIBLE_USERS_ARRAY, mVisibleUsers.keyAt(i));
            }
            proto.write(UserControllerProto.CURRENT_USER, mCurrentUserId);
            for (int i = 0; i < mCurrentProfileIds.length; i++) {
                proto.write(UserControllerProto.CURRENT_PROFILES, mCurrentProfileIds[i]);
@@ -3094,7 +2985,6 @@ class UserController implements Handler.Callback {
                pw.println("  mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
            }
            pw.println("  mLastUserUnlockingUptime: " + mLastUserUnlockingUptime);
            pw.println("  mVisibleUsers: " + mVisibleUsers);
        }
    }

@@ -3212,10 +3102,6 @@ class UserController implements Handler.Callback {
            case COMPLETE_USER_SWITCH_MSG:
                completeUserSwitch(msg.arg1);
                break;
            case USER_VISIBILITY_CHANGED_MSG:
                mInjector.onUserVisibilityChanged(/* userId= */ msg.arg1,
                        /* visible= */ msg.arg2 == 1);
                break;
        }
        return false;
    }
@@ -3750,8 +3636,8 @@ class UserController implements Handler.Callback {
            getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
        }

        void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
            getUserManagerInternal().onUserVisibilityChanged(userId, visible);
        void onSystemUserVisibilityChanged(boolean visible) {
            getUserManagerInternal().onSystemUserVisibilityChanged(visible);
        }
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -435,8 +435,10 @@ public abstract class UserManagerInternal {
    /** Removes a {@link UserVisibilityListener}. */
    public abstract void removeUserVisibilityListener(UserVisibilityListener listener);

    /** TODO(b/244333150): temporary method until UserVisibilityMediator handles that logic */
    public abstract void onUserVisibilityChanged(@UserIdInt int userId, boolean visible);
    // TODO(b/242195409): remove this method if not needed anymore
    /** Notify {@link UserVisibilityListener listeners} that the visibility of the
     * {@link android.os.UserHandle#USER_SYSTEM} changed. */
    public abstract void onSystemUserVisibilityChanged(boolean visible);

    /** Return the integer types of the given user IDs. Only used for reporting metrics to statsd.
     */
+4 −27
Original line number Diff line number Diff line
@@ -97,7 +97,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Slog;
@@ -126,7 +125,6 @@ import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
@@ -508,10 +506,6 @@ public class UserManagerService extends IUserManager.Stub {
    @GuardedBy("mUserLifecycleListeners")
    private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();

    // TODO(b/244333150): temporary array, should belong to UserVisibilityMediator
    @GuardedBy("mUserVisibilityListeners")
    private final ArrayList<UserVisibilityListener> mUserVisibilityListeners = new ArrayList<>();

    private final LockPatternUtils mLockPatternUtils;

    private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK =
@@ -6279,9 +6273,6 @@ public class UserManagerService extends IUserManager.Stub {
        synchronized (mUserLifecycleListeners) {
            pw.println("  user lifecycle events: " + mUserLifecycleListeners.size());
        }
        synchronized (mUserVisibilityListeners) {
            pw.println("  user visibility events: " + mUserVisibilityListeners.size());
        }

        // Dump UserTypes
        pw.println();
@@ -6854,31 +6845,17 @@ public class UserManagerService extends IUserManager.Stub {

        @Override
        public void addUserVisibilityListener(UserVisibilityListener listener) {
            synchronized (mUserVisibilityListeners) {
                mUserVisibilityListeners.add(listener);
            }
            mUserVisibilityMediator.addListener(listener);
        }

        @Override
        public void removeUserVisibilityListener(UserVisibilityListener listener) {
            synchronized (mUserVisibilityListeners) {
                mUserVisibilityListeners.remove(listener);
            }
            mUserVisibilityMediator.removeListener(listener);
        }

        @Override
        public void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
            EventLog.writeEvent(EventLogTags.UM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
            mHandler.post(() -> {
                UserVisibilityListener[] listeners;
                synchronized (mUserVisibilityListeners) {
                    listeners = new UserVisibilityListener[mUserVisibilityListeners.size()];
                    mUserVisibilityListeners.toArray(listeners);
                }
                for (UserVisibilityListener listener : listeners) {
                    listener.onUserVisibilityChanged(userId, visible);
                }
            });
        public void onSystemUserVisibilityChanged(boolean visible) {
            mUserVisibilityMediator.onSystemUserVisibilityChanged(visible);
        }

        @Override
+16 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Dumpable;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.SparseIntArray;
@@ -40,6 +41,7 @@ import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.am.EventLogTags;
import com.android.server.pm.UserManagerInternal.UserAssignmentResult;
import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
import com.android.server.utils.Slogf;
@@ -68,6 +70,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public final class UserVisibilityMediator implements Dumpable {

    private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
    private static final boolean VERBOSE = false; // DO NOT SUBMIT WITH TRUE

    private static final String TAG = UserVisibilityMediator.class.getSimpleName();

@@ -381,8 +384,8 @@ public final class UserVisibilityMediator implements Dumpable {
    public boolean isUserVisible(@UserIdInt int userId) {
        // First check current foreground user and their profiles (on main display)
        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
            if (DBG) {
                Slogf.d(TAG, "isUserVisible(%d): true to current user or profile", userId);
            if (VERBOSE) {
                Slogf.v(TAG, "isUserVisible(%d): true to current user or profile", userId);
            }
            return true;
        }
@@ -517,6 +520,14 @@ public final class UserVisibilityMediator implements Dumpable {
        }
    }

    // TODO(b/242195409): remove this method if not needed anymore
    /**
     * Nofify all listeners that the system user visibility changed.
     */
    void onSystemUserVisibilityChanged(boolean visible) {
        dispatchVisibilityChanged(mListeners, USER_SYSTEM, visible);
    }

    /**
     * Nofify all listeners about the visibility changes from before / after a change of state.
     */
@@ -534,7 +545,7 @@ public final class UserVisibilityMediator implements Dumpable {
            Slogf.d(TAG,
                    "dispatchVisibilityChanged(): visibleUsersBefore=%s, visibleUsersAfter=%s, "
                    + "%d listeners (%s)", visibleUsersBefore, visibleUsersAfter, listeners.size(),
                    mListeners);
                    listeners);
        }
        for (int i = 0; i < visibleUsersBefore.size(); i++) {
            int userId = visibleUsersBefore.get(i);
@@ -552,13 +563,14 @@ public final class UserVisibilityMediator implements Dumpable {

    private void dispatchVisibilityChanged(CopyOnWriteArrayList<UserVisibilityListener> listeners,
            @UserIdInt int userId, boolean visible) {
        EventLog.writeEvent(EventLogTags.UM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
        if (DBG) {
            Slogf.d(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %d listeners",
                    userId, visible, listeners.size());
        }
        for (int i = 0; i < mListeners.size(); i++) {
            UserVisibilityListener listener =  mListeners.get(i);
            if (DBG) {
            if (VERBOSE) {
                Slogf.v(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %s",
                        userId, visible, listener);
            }
Loading