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

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

Merge "More minor improvements on users on secondary display APIs."

parents 9a823810 5cb4bf9d
Loading
Loading
Loading
Loading
+23 −7
Original line number Diff line number Diff line
@@ -4360,20 +4360,28 @@ public class ActivityManager {
    }

    /**
     * Starts the given user in background and associate the user with the given display.
     * Starts the given user in background and assign the user to the given display.
     *
     * <p>This method will allow the user to launch activities on that display, and it's typically
     * used only on automotive builds when the vehicle has multiple displays (you can verify if it's
     * supported by calling {@link UserManager#isBackgroundUsersOnSecondaryDisplaysSupported()}).
     * supported by calling {@link UserManager#isUsersOnSecondaryDisplaysSupported()}).
     *
     * @return whether the user was started.
     * <p><b>NOTE:</b> differently from {@link #switchUser(int)}, which stops the current foreground
     * user before starting a new one, this method does not stop the previous user running in
     * background in the display, and it will return {@code false} in this case. It's up to the
     * caller to call {@link #stopUser(int, boolean)} before starting a new user.
     *
     * @param userId user to be started in the display. It will return {@code false} if the user is
     * a profile, the {@link #getCurrentUser()}, the {@link UserHandle#SYSTEM system user}, or
     * does not exist.
     *
     * @param displayId id of the display, it must exist.
     *
     * @return whether the operation succeeded. Notice that if the user was already started in such
     * display before, it will return {@code false}.
     *
     * @throws UnsupportedOperationException if the device does not support background users on
     * secondary displays.
     * @throws IllegalArgumentException if the display does not exist.
     * @throws IllegalStateException if the user cannot be started on that display (for example, if
     * there's already a user using that display or if the user is already associated with other
     * display).
     *
     * @hide
     */
@@ -4382,6 +4390,10 @@ public class ActivityManager {
            android.Manifest.permission.CREATE_USERS})
    public boolean startUserInBackgroundOnSecondaryDisplay(@UserIdInt int userId,
            int displayId) {
        if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
            throw new UnsupportedOperationException(
                    "device does not support users on secondary displays");
        }
        try {
            return getService().startUserInBackgroundOnSecondaryDisplay(userId, displayId);
        } catch (RemoteException e) {
@@ -4542,6 +4554,10 @@ public class ActivityManager {
    /**
     * Stops the given {@code userId}.
     *
     * <p><b>NOTE:</b> on systems that support
     * {@link UserManager#isUsersOnSecondaryDisplaysSupported() background users on secondary
     * displays}, this method will also unassign the user from the display it was started on.
     *
     * @hide
     */
    @TestApi
+2 −0
Original line number Diff line number Diff line
@@ -766,6 +766,8 @@ interface IActivityManager {
     *
     * <p>Typically used only by automotive builds when the vehicle has multiple displays.
     */
    @JavaPassthrough(annotation=
            "@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional = true)")
    boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId);

}
+18 −10
Original line number Diff line number Diff line
@@ -328,8 +328,10 @@ public abstract class UserManagerInternal {
     * <p>On most devices this call will be a no-op, but it will be used on devices that support
     * multiple users on multiple displays (like automotives with passenger displays).
     *
     * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
     * started and it doesn't validate if the display exists.
     * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to
     * check it. In fact, one of the intended clients for this method is
     * {@code DisplayManagerService}, which will call it when a virtual display is created (another
     * client is {@code UserController}, which will call it when a user is started).
     *
     */
    public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
@@ -340,8 +342,8 @@ public abstract class UserManagerInternal {
     * <p>On most devices this call will be a no-op, but it will be used on devices that support
     * multiple users on multiple displays (like automotives with passenger displays).
     *
     * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
     * stopped.
     * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user
     * is stopped) and {@code DisplayManagerService} (when a virtual display is destroyed).
     */
    public abstract void unassignUserFromDisplay(@UserIdInt int userId);

@@ -361,11 +363,14 @@ public abstract class UserManagerInternal {
     * Returns the display id assigned to the user, or {@code Display.INVALID_DISPLAY} if the
     * user is not assigned to any display.
     *
     * <p>The current foreground user is associated with the
     * <p>The current foreground user and its running profiles are associated with the
     * {@link android.view.Display#DEFAULT_DISPLAY default display}, while other users would only be
     * assigned to a display if they were started with
     * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}. If the user is a profile
     * and is running, it's assigned to its parent display.
     * assigned to a display if a call to {@link #assignUserToDisplay(int, int)} is made for such
     * user / display combination (for example, if the user was started with
     * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}, {@code UserController}
     * would make such call).
     *
     * <p>If the user is a profile and is running, it's assigned to its parent display.
     */
    public abstract int getDisplayAssignedToUser(@UserIdInt int userId);

@@ -375,8 +380,11 @@ public abstract class UserManagerInternal {
     * associated with the display.
     *
     * <p>The {@link android.view.Display#DEFAULT_DISPLAY default display} is always assigned to
     * the current foreground user, while other displays would be associated with the user that was
     * started with {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}.
     * the current foreground user, while other displays would only be associated with users through
     * a explicit {@link #assignUserToDisplay(int, int)} call with that user / display combination
     * (for example, if the user was started with
     * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}, {@code UserController}
     * would make such call).
     */
    public abstract @UserIdInt int getUserAssignedToDisplay(int displayId);
}
+16 −22
Original line number Diff line number Diff line
@@ -1785,18 +1785,10 @@ public class UserManagerService extends IUserManager.Stub {

    @VisibleForTesting
    int getUserAssignedToDisplay(int displayId) {
        if (displayId == Display.DEFAULT_DISPLAY) {
        if (displayId == Display.DEFAULT_DISPLAY || !mUsersOnSecondaryDisplaysEnabled) {
            return getCurrentUserId();
        }

        if (!mUsersOnSecondaryDisplaysEnabled) {
            int currentUserId = getCurrentUserId();
            Slogf.w(LOG_TAG, "getUsersAssignedToDisplay(%d) called with non-DEFAULT_DISPLAY on "
                    + "system that doesn't support that; returning current user (%d)", displayId,
                    currentUserId);
            return currentUserId;
        }

        synchronized (mUsersOnSecondaryDisplays) {
            for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
                if (mUsersOnSecondaryDisplays.valueAt(i) != displayId) {
@@ -6844,20 +6836,22 @@ public class UserManagerService extends IUserManager.Stub {

                // Check if display is available
                for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
                    // Make sure display is not used by other users...
                    // TODO(b/240736142); currently, if a user was started in a display, it
                    // would need to be stopped first, so "switching" a user on secondary
                    // diplay requires 2 non-atomic operations (stop and start). Once this logic
                    // is refactored, it should be atomic.
                    if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) {
                        throw new IllegalStateException("Cannot assign " + userId + " to "
                                + "display " + displayId + " as it's already assigned to "
                                + "user " + mUsersOnSecondaryDisplays.keyAt(i));
                    }
                    // TODO(b/239982558) also check that user is not already assigned to other
                    // display (including 0). That would be harder to tested under CTS though
                    // (for example, would need to add a new AM method to start user in bg on
                    // main display), so it's better to test on unit tests
                    int assignedUserId = mUsersOnSecondaryDisplays.keyAt(i);
                    int assignedDisplayId = mUsersOnSecondaryDisplays.valueAt(i);
                    if (DBG_MUMD) {
                        Slogf.d(LOG_TAG, "%d: assignedUserId=%d, assignedDisplayId=%d",
                                i, assignedUserId, assignedDisplayId);
                    }
                    if (displayId == assignedDisplayId) {
                        throw new IllegalStateException("Cannot assign user " + userId + " to "
                                + "display " + displayId + " because such display is already "
                                + "assigned to user " + assignedUserId);
                    }
                    if (userId == assignedUserId) {
                        throw new IllegalStateException("Cannot assign user " + userId + " to "
                                + "display " + displayId + " because such user is as already "
                                + "assigned to display " + assignedDisplayId);
                    }
                }

                if (DBG_MUMD) {
+17 −0
Original line number Diff line number Diff line
@@ -162,6 +162,23 @@ public final class UserManagerInternalTest extends UserManagerServiceOrInternalT
                        + USER_ID + ".*");
    }

    @Test
    public void testAssignUserToDisplay_userAlreadyAssigned() {
        enableUsersOnSecondaryDisplays();

        mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);

        IllegalStateException e = assertThrows(IllegalStateException.class,
                () -> mUmi.assignUserToDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID));

        Log.v(TAG, "Exception: " + e);
        assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
                .matches("Cannot.*" + USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID + ".*already.*"
                        + SECONDARY_DISPLAY_ID + ".*");

        assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
    }

    @Test
    public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
        enableUsersOnSecondaryDisplays();