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

Commit 1a8c7c14 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Revert "Revert "Fixed UserVisibilityMediator profile scenario.""" into udc-dev

parents 2181d8da 1ff8deb9
Loading
Loading
Loading
Loading
+30 −5
Original line number Diff line number Diff line
@@ -64,12 +64,11 @@ public abstract class UserManagerInternal {
    })
    public @interface UserAssignmentResult {}

    // TODO(b/248408342): Move keep annotation to the method referencing these fields reflectively.
    @Keep public static final int USER_START_MODE_FOREGROUND = 1;
    @Keep public static final int USER_START_MODE_BACKGROUND = 2;
    @Keep public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;

    private static final String PREFIX_USER_START_MODE = "USER_START_MODE_";

    /**
     * Type used to indicate how a user started.
     */
    @IntDef(flag = false, prefix = {PREFIX_USER_START_MODE}, value = {
            USER_START_MODE_FOREGROUND,
            USER_START_MODE_BACKGROUND,
@@ -77,6 +76,32 @@ public abstract class UserManagerInternal {
    })
    public @interface UserStartMode {}

    // TODO(b/248408342): Move keep annotations below to the method referencing these fields
    // reflectively.

    /** (Full) user started on foreground (a.k.a. "current user"). */
    @Keep public static final int USER_START_MODE_FOREGROUND = 1;

    /**
     * User (full or profile) started on background and is
     * {@link UserManager#isUserVisible() invisible}.
     *
     * <p>This is the "traditional" way of starting a background user, and can be used to start
     * profiles as well, although starting an invisible profile is not common from the System UI
     * (it could be done through APIs or adb, though).
     */
    @Keep public static final int USER_START_MODE_BACKGROUND = 2;

    /**
     * User (full or profile) started on background and is
     * {@link UserManager#isUserVisible() visible}.
     *
     * <p>This is the "traditional" way of starting a profile (i.e., when the profile of the current
     * user is the current foreground user), but it can also be used to start a full user associated
     * with a display (which is the case on automotives with passenger displays).
     */
    @Keep public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;

    public interface UserRestrictionsListener {
        /**
         * Called when a user restriction changes.
+153 −68
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.util.Dumpable;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Display;

@@ -55,6 +56,8 @@ import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
import com.android.server.utils.Slogf;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
@@ -77,11 +80,11 @@ 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();

    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean VERBOSE = false; // DO NOT SUBMIT WITH TRUE

    private static final String PREFIX_SECONDARY_DISPLAY_MAPPING = "SECONDARY_DISPLAY_MAPPING_";
    public static final int SECONDARY_DISPLAY_MAPPING_NEEDED = 1;
    public static final int SECONDARY_DISPLAY_MAPPING_NOT_NEEDED = 2;
@@ -98,7 +101,7 @@ public final class UserVisibilityMediator implements Dumpable {
    })
    public @interface SecondaryDisplayMappingStatus {}

    // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
    // TODO(b/266158156): might need to change this if boot logic is refactored for HSUM devices
    @VisibleForTesting
    static final int INITIAL_CURRENT_USER_ID = USER_SYSTEM;

@@ -132,10 +135,23 @@ public final class UserVisibilityMediator implements Dumpable {
    private final SparseIntArray mExtraDisplaysAssignedToUsers;

    /**
     * Mapping from each started user to its profile group.
     * Mapping of each user that started visible (key) to its profile group id (value).
     *
     * <p>It's used to determine not just if the user is visible, but also
     * {@link #isProfile(int, int) if it's a profile}.
     */
    @GuardedBy("mLock")
    private final SparseIntArray mStartedVisibleProfileGroupIds = new SparseIntArray();

    /**
     * List of profiles that have explicitly started invisible.
     *
     * <p>Only used for debugging purposes (and set when {@link #DBG} is {@code true}), hence we
     * don't care about autoboxing.
     */
    @GuardedBy("mLock")
    private final SparseIntArray mStartedProfileGroupIds = new SparseIntArray();
    @Nullable
    private final List<Integer> mStartedInvisibleProfileUserIds;

    /**
     * Handler user to call listeners
@@ -164,9 +180,14 @@ public final class UserVisibilityMediator implements Dumpable {
            mUsersAssignedToDisplayOnStart = null;
            mExtraDisplaysAssignedToUsers = null;
        }
        mStartedInvisibleProfileUserIds = DBG ? new ArrayList<>(4) : null;
        mHandler = handler;
        // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
        mStartedProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID);
        // TODO(b/266158156): might need to change this if boot logic is refactored for HSUM devices
        mStartedVisibleProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID);

        if (DBG) {
            Slogf.i(TAG, "UserVisibilityMediator created with DBG on");
        }
    }

    /**
@@ -177,6 +198,8 @@ public final class UserVisibilityMediator implements Dumpable {
            int displayId) {
        Preconditions.checkArgument(!isSpecialUserId(userId), "user id cannot be generic: %d",
                userId);
        validateUserStartMode(userStartMode);

        // This method needs to perform 4 actions:
        //
        // 1. Check if the user can be started given the provided arguments
@@ -224,14 +247,29 @@ public final class UserVisibilityMediator implements Dumpable {

            visibleUsersBefore = getVisibleUsers();

            // Set current user / profiles state
            if (userStartMode == USER_START_MODE_FOREGROUND) {
            // Set current user / started users state
            switch (userStartMode) {
                case USER_START_MODE_FOREGROUND:
                    mCurrentUserId = userId;
            }
                    // Fallthrough
                case USER_START_MODE_BACKGROUND_VISIBLE:
                    if (DBG) {
                Slogf.d(TAG, "adding user / profile mapping (%d -> %d)", userId, profileGroupId);
                        Slogf.d(TAG, "adding visible user / profile group id mapping (%d -> %d)",
                                userId, profileGroupId);
                    }
                    mStartedVisibleProfileGroupIds.put(userId, profileGroupId);
                    break;
                case USER_START_MODE_BACKGROUND:
                    if (mStartedInvisibleProfileUserIds != null
                            && isProfile(userId, profileGroupId)) {
                        Slogf.d(TAG, "adding user %d to list of invisible profiles", userId);
                        mStartedInvisibleProfileUserIds.add(userId);
                    }
                    break;
                default:
                    Slogf.wtf(TAG,  "invalid userStartMode passed to assignUserToDisplayOnStart: "
                            + "%d", userStartMode);
            }
            mStartedProfileGroupIds.put(userId, profileGroupId);

            //  Set user / display state
            switch (mappingResult) {
@@ -297,38 +335,44 @@ public final class UserVisibilityMediator implements Dumpable {
        boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
        if (displayId != DEFAULT_DISPLAY) {
            if (foreground) {
                Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %b, %d) failed: cannot start "
                Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: cannot start "
                        + "foreground user on secondary display", userId, profileGroupId,
                        foreground, displayId);
                        userStartModeToString(userStartMode), displayId);
                return USER_ASSIGNMENT_RESULT_FAILURE;
            }
            if (!mVisibleBackgroundUsersEnabled) {
                Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %b, %d) failed: called on "
                Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: called on "
                        + "device that doesn't support multiple users on multiple displays",
                        userId, profileGroupId, foreground, displayId);
                        userId, profileGroupId, userStartModeToString(userStartMode), displayId);
                return USER_ASSIGNMENT_RESULT_FAILURE;
            }
        }

        if (isProfile(userId, profileGroupId)) {
            if (displayId != DEFAULT_DISPLAY) {
                Slogf.w(TAG, "canStartUserLocked(%d, %d, %b, %d) failed: cannot start profile user "
                        + "on secondary display", userId, profileGroupId, foreground,
                        displayId);
                Slogf.w(TAG, "canStartUserLocked(%d, %d, %s, %d) failed: cannot start profile user "
                        + "on secondary display", userId, profileGroupId,
                        userStartModeToString(userStartMode), displayId);
                return USER_ASSIGNMENT_RESULT_FAILURE;
            }
            if (foreground) {
                Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user in "
                        + "foreground", userId, profileGroupId, foreground, displayId);
            switch (userStartMode) {
                case USER_START_MODE_FOREGROUND:
                    Slogf.w(TAG, "startUser(%d, %d, %s, %d) failed: cannot start profile user in "
                            + "foreground", userId, profileGroupId,
                            userStartModeToString(userStartMode), displayId);
                    return USER_ASSIGNMENT_RESULT_FAILURE;
            } else {
                case USER_START_MODE_BACKGROUND_VISIBLE:
                    boolean isParentVisibleOnDisplay = isUserVisible(profileGroupId, displayId);
                if (DBG) {
                    Slogf.d(TAG, "parent visible on display: %b", isParentVisibleOnDisplay);
                    if (!isParentVisibleOnDisplay) {
                        Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: cannot"
                                + " start profile user visible when its parent is not visible in "
                                + "that display", userId, profileGroupId,
                                userStartModeToString(userStartMode), displayId);
                        return USER_ASSIGNMENT_RESULT_FAILURE;
                    }
                return isParentVisibleOnDisplay
                        ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
                        : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
                    return USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
                case USER_START_MODE_BACKGROUND:
                    return USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
            }
        } else if (mUsersAssignedToDisplayOnStart != null
                && isUserAssignedToDisplayOnStartLocked(userId, displayId)) {
@@ -353,8 +397,9 @@ public final class UserVisibilityMediator implements Dumpable {
            if (mVisibleBackgroundUserOnDefaultDisplayAllowed
                    && userStartMode == USER_START_MODE_BACKGROUND_VISIBLE) {
                int userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
                if (userStartedOnDefaultDisplay != USER_NULL) {
                    Slogf.w(TAG, "getUserVisibilityOnStartLocked(): cannot start user %d visible on"
                if (userStartedOnDefaultDisplay != USER_NULL
                        && userStartedOnDefaultDisplay != profileGroupId) {
                    Slogf.w(TAG, "canAssignUserToDisplayLocked(): cannot start user %d visible on"
                            + " default display because user %d already did so", userId,
                            userStartedOnDefaultDisplay);
                    return SECONDARY_DISPLAY_MAPPING_FAILED;
@@ -468,7 +513,7 @@ public final class UserVisibilityMediator implements Dumpable {
                        userId, displayId);
                return false;
            }
            if (isStartedProfile(userId)) {
            if (isStartedVisibleProfileLocked(userId)) {
                Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user is a profile",
                        userId, displayId);
                return false;
@@ -556,10 +601,14 @@ public final class UserVisibilityMediator implements Dumpable {
    @GuardedBy("mLock")
    private void unassignUserFromAllDisplaysOnStopLocked(@UserIdInt int userId) {
        if (DBG) {
            Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId,
                    mStartedProfileGroupIds);
            Slogf.d(TAG, "Removing %d from mStartedVisibleProfileGroupIds (%s)", userId,
                    mStartedVisibleProfileGroupIds);
        }
        mStartedVisibleProfileGroupIds.delete(userId);
        if (mStartedInvisibleProfileUserIds != null) {
            Slogf.d(TAG, "Removing %d from list of invisible profiles", userId);
            mStartedInvisibleProfileUserIds.remove(Integer.valueOf(userId));
        }
        mStartedProfileGroupIds.delete(userId);

        if (!mVisibleBackgroundUsersEnabled) {
            // Don't need to update mUsersAssignedToDisplayOnStart because methods (such as
@@ -589,7 +638,8 @@ public final class UserVisibilityMediator implements Dumpable {
     * See {@link UserManagerInternal#isUserVisible(int)}.
     */
    public boolean isUserVisible(@UserIdInt int userId) {
        // First check current foreground user and their profiles (on main display)
        // For optimization (as most devices don't support visible background users), check for
        // current foreground user and their profiles first
        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
            if (VERBOSE) {
                Slogf.v(TAG, "isUserVisible(%d): true to current user or profile", userId);
@@ -598,19 +648,31 @@ public final class UserVisibilityMediator implements Dumpable {
        }

        if (!mVisibleBackgroundUsersEnabled) {
            if (DBG) {
                Slogf.d(TAG, "isUserVisible(%d): false for non-current user (or its profiles) when"
            if (VERBOSE) {
                Slogf.v(TAG, "isUserVisible(%d): false for non-current user (or its profiles) when"
                        + " device doesn't support visible background users", userId);
            }
            return false;
        }

        boolean visible;

        synchronized (mLock) {
            int profileGroupId;
            synchronized (mLock) {
            visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0;
                profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
            }
            if (isProfile(userId, profileGroupId)) {
                return isUserAssignedToDisplayOnStartLocked(profileGroupId);
            }
            return isUserAssignedToDisplayOnStartLocked(userId);
        }
        if (DBG) {
            Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible);
    }

    @GuardedBy("mLock")
    private boolean isUserAssignedToDisplayOnStartLocked(@UserIdInt int userId) {
        boolean visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0;
        if (VERBOSE) {
            Slogf.v(TAG, "isUserAssignedToDisplayOnStartLocked(%d): %b", userId, visible);
        }
        return visible;
    }
@@ -640,7 +702,8 @@ public final class UserVisibilityMediator implements Dumpable {
            return false;
        }

        // Current user is always visible on:
        // For optimization (as most devices don't support visible background users), check for
        // current user and profile first. Current user is always visible on:
        // - Default display
        // - Secondary displays when device doesn't support visible bg users
        //   - Or when explicitly added (which is checked below)
@@ -662,15 +725,27 @@ public final class UserVisibilityMediator implements Dumpable {
        }

        synchronized (mLock) {
            int profileGroupId;
            synchronized (mLock) {
                profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
            }
            if (isProfile(userId, profileGroupId)) {
                return isFullUserVisibleOnBackgroundLocked(profileGroupId, displayId);
            }
            return isFullUserVisibleOnBackgroundLocked(userId, displayId);
        }
    }

    // NOTE: it doesn't check if the userId is a full user, it's up to the caller to check that
    @GuardedBy("mLock")
    private boolean isFullUserVisibleOnBackgroundLocked(@UserIdInt int userId, int displayId) {
        if (mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY) == displayId) {
            // User assigned to display on start
            return true;
        }

        // Check for extra display assignment
        return mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId;
    }
    }

    /**
     * See {@link UserManagerInternal#getDisplayAssignedToUser(int)}.
@@ -737,7 +812,7 @@ public final class UserVisibilityMediator implements Dumpable {
                    continue;
                }
                int userId = mUsersAssignedToDisplayOnStart.keyAt(i);
                if (!isStartedProfile(userId)) {
                if (!isStartedVisibleProfileLocked(userId)) {
                    return userId;
                } else if (DBG) {
                    Slogf.d(TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
@@ -770,8 +845,8 @@ public final class UserVisibilityMediator implements Dumpable {
        // number of users is too small, the gain is probably not worth the increase on complexity.
        IntArray visibleUsers = new IntArray();
        synchronized (mLock) {
            for (int i = 0; i < mStartedProfileGroupIds.size(); i++) {
                int userId = mStartedProfileGroupIds.keyAt(i);
            for (int i = 0; i < mStartedVisibleProfileGroupIds.size(); i++) {
                int userId = mStartedVisibleProfileGroupIds.keyAt(i);
                if (isUserVisible(userId)) {
                    visibleUsers.add(userId);
                }
@@ -804,7 +879,7 @@ public final class UserVisibilityMediator implements Dumpable {
        }
    }

    // TODO(b/242195409): remove this method if not needed anymore
    // TODO(b/266158156): remove this method if not needed anymore
    /**
     * Nofify all listeners that the system user visibility changed.
     */
@@ -866,6 +941,9 @@ public final class UserVisibilityMediator implements Dumpable {
        ipw.println("UserVisibilityMediator");
        ipw.increaseIndent();

        ipw.print("DBG: ");
        ipw.println(DBG);

        synchronized (mLock) {
            ipw.print("Current user id: ");
            ipw.println(mCurrentUserId);
@@ -873,8 +951,12 @@ public final class UserVisibilityMediator implements Dumpable {
            ipw.print("Visible users: ");
            ipw.println(getVisibleUsers());

            dumpSparseIntArray(ipw, mStartedProfileGroupIds, "started user / profile group",
                    "u", "pg");
            dumpSparseIntArray(ipw, mStartedVisibleProfileGroupIds,
                    "started visible user / profile group", "u", "pg");
            if (mStartedInvisibleProfileUserIds != null) {
                ipw.print("Profiles started invisible: ");
                ipw.println(mStartedInvisibleProfileUserIds);
            }

            ipw.print("Supports visible background users on displays: ");
            ipw.println(mVisibleBackgroundUsersEnabled);
@@ -982,22 +1064,25 @@ public final class UserVisibilityMediator implements Dumpable {
            if (mCurrentUserId == userId) {
                return true;
            }
            return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID) == mCurrentUserId;
            return mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID)
                    == mCurrentUserId;
        }
    }

    private boolean isStartedProfile(@UserIdInt int userId) {
        int profileGroupId;
        synchronized (mLock) {
            profileGroupId = mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
        }
    @GuardedBy("mLock")
    private boolean isStartedVisibleProfileLocked(@UserIdInt int userId) {
        int profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
        return isProfile(userId, profileGroupId);
    }

    private @UserIdInt int getStartedProfileGroupId(@UserIdInt int userId) {
        synchronized (mLock) {
            return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
    private void validateUserStartMode(@UserStartMode int userStartMode) {
        switch (userStartMode) {
            case USER_START_MODE_FOREGROUND:
            case USER_START_MODE_BACKGROUND:
            case USER_START_MODE_BACKGROUND_VISIBLE:
                return;
        }
        throw new IllegalArgumentException("Invalid user start mode: " + userStartMode);
    }

    private static String secondaryDisplayMappingStatusToString(
+6 −7
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;

import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
import static com.android.server.pm.UserVisibilityChangedEvent.onInvisible;
import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
@@ -73,8 +75,8 @@ public final class UserVisibilityMediatorMUPANDTest
        assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);

        // Make sure another user cannot be started on default display
        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
                BG_VISIBLE, DEFAULT_DISPLAY);
        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, otherUserId, BG_VISIBLE,
                DEFAULT_DISPLAY);
        assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
                "when user (%d) is starting on default display after it was started by user %d",
                otherUserId, visibleBgUserId);
@@ -117,8 +119,8 @@ public final class UserVisibilityMediatorMUPANDTest
        assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);

        // Make sure another user cannot be started on default display
        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
                BG_VISIBLE, DEFAULT_DISPLAY);
        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, otherUserId, BG_VISIBLE,
                DEFAULT_DISPLAY);
        assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
                "when user (%d) is starting on default display after it was started by user %d",
                otherUserId, visibleBgUserId);
@@ -127,8 +129,6 @@ public final class UserVisibilityMediatorMUPANDTest
        listener.verify();
    }

  /* TODO(b/261538337): re-add after the reverted CL is merged again

    @Test
    public void
       testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnBgOnSecondaryDisplay()
@@ -226,5 +226,4 @@ public final class UserVisibilityMediatorMUPANDTest

        listener.verify();
    }
  */
}
+44 −6

File changed.

Preview size limit exceeded, changes collapsed.

+71 −30

File changed.

Preview size limit exceeded, changes collapsed.