From 9474490572fc669bc0f55c3818ed510b575f605b Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Fri, 16 Dec 2022 14:15:19 -0800 Subject: [PATCH] New UserManagerInternal APIs for extra user / display assignment. Currently, a user is only visible in one display, which is assigned when the user is started. But there will be cases where the user should be visible on multiple displays, like on cars where the back passenger seats have 2 displays each (one "main" display and a smaller display for user input) Test: atest UserVisibilityMediatorSUSDTest UserVisibilityMediatorMUMDTest Bug: 261917465 Change-Id: I824f9663ef83451239a94f656eadba138a6ae50f --- .../server/pm/UserManagerInternal.java | 36 +++- .../android/server/pm/UserManagerService.java | 10 + .../server/pm/UserVisibilityMediator.java | 199 ++++++++++++++++-- .../pm/UserVisibilityMediatorSUSDTest.java | 3 + .../pm/UserVisibilityMediatorTestCase.java | 99 ++++++++- ...MediatorVisibleBackgroundUserTestCase.java | 57 ++++- 6 files changed, 369 insertions(+), 35 deletions(-) diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index 2ae8b52da172f..7b15e760107bb 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -388,8 +388,8 @@ public abstract class UserManagerInternal { * and the user is {@link UserManager#isUserVisible() visible}. * *

NOTE: this method is meant to be used only by {@code UserController} (when a user - * is started). If other clients (like {@code CarService} need to explicitly change the user / - * display assignment, we'll need to provide other APIs. + * is started); for extra unassignments, callers should call {@link + * #assignUserToExtraDisplay(int, int)} instead. * *

NOTE: this method doesn't validate if the display exists, it's up to the caller to * pass a valid display id. @@ -397,15 +397,43 @@ public abstract class UserManagerInternal { public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId); + /** + * Assigns an extra display to the given user, so the user is visible on that display. + * + *

This method is meant to be used on automotive builds where a passenger zone has more than + * one display (for example, the "main" display and a smaller display used for input). + * + *

NOTE: this call will be ignored on devices that do not + * {@link UserManager#isVisibleBackgroundUsersSupported() support visible background users}. + * + * @return whether the operation succeeded, in which case the user would be visible on the + * display. + */ + public abstract boolean assignUserToExtraDisplay(@UserIdInt int userId, int displayId); + /** * Unassigns a user from its current display when it's stopping. * *

NOTE: this method is meant to be used only by {@code UserController} (when a user - * is stopped). If other clients (like {@code CarService} need to explicitly change the user / - * display assignment, we'll need to provide other APIs. + * is stopped); for extra unassignments, callers should call + * {@link #unassignUserFromExtraDisplay(int, int)} instead. */ public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); + /** + * Unassigns the extra display from the given user. + * + *

This method is meant to be used on automotive builds where a passenger zone has more than + * one display (for example, the "main" display and a smaller display used for input). + * + *

NOTE: this call will be ignored on devices that do not + * {@link UserManager#isVisibleBackgroundUsersSupported() support visible background users}. + * + * @return whether the operation succeeded, i.e., the user was previously + * {@link #assignUserToExtraDisplay(int, int) assigned to an extra display}. + */ + public abstract boolean unassignUserFromExtraDisplay(@UserIdInt int userId, int displayId); + /** * Returns {@code true} if the user is visible (as defined by * {@link UserManager#isUserVisible()}. diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 6bac90532645b..3f8f711a4640c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -7014,6 +7014,16 @@ public class UserManagerService extends IUserManager.Stub { userStartMode, displayId); } + @Override + public boolean assignUserToExtraDisplay(int userId, int displayId) { + return mUserVisibilityMediator.assignUserToExtraDisplay(userId, displayId); + } + + @Override + public boolean unassignUserFromExtraDisplay(int userId, int displayId) { + return mUserVisibilityMediator.unassignUserFromExtraDisplay(userId, displayId); + } + @Override public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { mUserVisibilityMediator.unassignUserFromDisplayOnStop(userId); diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java index d8e4dac48ebef..66d390f4f3e99 100644 --- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java +++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java @@ -19,6 +19,7 @@ import static android.content.pm.UserInfo.NO_PROFILE_GROUP_ID; import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; 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_INVISIBLE; @@ -113,7 +114,18 @@ public final class UserVisibilityMediator implements Dumpable { */ @Nullable @GuardedBy("mLock") - private final SparseIntArray mUsersOnDisplaysMap; + private final SparseIntArray mUsersAssignedToDisplayOnStart; + + /** + * Map of extra (i.e., not assigned on start, but by explicit calls to + * {@link #assignUserToExtraDisplay(int, int)}) displays assigned to user (key is display id, + * value is user id). + * + *

Only set when {@code mUsersOnSecondaryDisplaysEnabled} is {@code true}. + */ + @Nullable + @GuardedBy("mLock") + private final SparseIntArray mExtraDisplaysAssignedToUsers; /** * Mapping from each started user to its profile group. @@ -137,7 +149,13 @@ public final class UserVisibilityMediator implements Dumpable { @VisibleForTesting UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) { mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled; - mUsersOnDisplaysMap = mVisibleBackgroundUsersEnabled ? new SparseIntArray() : null; + if (mVisibleBackgroundUsersEnabled) { + mUsersAssignedToDisplayOnStart = new SparseIntArray(); + mExtraDisplaysAssignedToUsers = new SparseIntArray(); + } else { + mUsersAssignedToDisplayOnStart = null; + mExtraDisplaysAssignedToUsers = 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); @@ -207,7 +225,7 @@ public final class UserVisibilityMediator implements Dumpable { if (DBG) { Slogf.d(TAG, "adding user / display mapping (%d -> %d)", userId, displayId); } - mUsersOnDisplaysMap.put(userId, displayId); + mUsersAssignedToDisplayOnStart.put(userId, displayId); break; case SECONDARY_DISPLAY_MAPPING_NOT_NEEDED: if (DBG) { @@ -341,9 +359,9 @@ public final class UserVisibilityMediator implements Dumpable { } // Check if display is available - for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { - int assignedUserId = mUsersOnDisplaysMap.keyAt(i); - int assignedDisplayId = mUsersOnDisplaysMap.valueAt(i); + for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { + int assignedUserId = mUsersAssignedToDisplayOnStart.keyAt(i); + int assignedDisplayId = mUsersAssignedToDisplayOnStart.valueAt(i); if (DBG) { Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d", i, assignedUserId, assignedDisplayId); @@ -362,6 +380,100 @@ public final class UserVisibilityMediator implements Dumpable { return SECONDARY_DISPLAY_MAPPING_NEEDED; } + /** + * See {@link UserManagerInternal#assignUserToExtraDisplay(int, int)}. + */ + public boolean assignUserToExtraDisplay(@UserIdInt int userId, int displayId) { + if (DBG) { + Slogf.d(TAG, "assignUserToExtraDisplay(%d, %d)", userId, displayId); + } + if (!mVisibleBackgroundUsersEnabled) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): called when not supported", userId, + displayId); + return false; + } + if (displayId == INVALID_DISPLAY) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): called with INVALID_DISPLAY", userId, + displayId); + return false; + } + if (displayId == DEFAULT_DISPLAY) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): DEFAULT_DISPLAY is automatically " + + "assigned to current user", userId, displayId); + return false; + } + + synchronized (mLock) { + if (!isUserVisible(userId)) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user is not visible", + userId, displayId); + return false; + } + if (isStartedProfile(userId)) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user is a profile", + userId, displayId); + return false; + } + + if (mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user is already " + + "assigned to that display", userId, displayId); + return false; + } + + int userAssignedToDisplay = getUserAssignedToDisplay(displayId, + /* returnCurrentUserByDefault= */ false); + if (userAssignedToDisplay != USER_NULL) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because display was assigned" + + " to user %d on start", userId, displayId, userAssignedToDisplay); + return false; + } + userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(userId, USER_NULL); + if (userAssignedToDisplay != USER_NULL) { + Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user %d was already " + + "assigned that extra display", userId, displayId, userAssignedToDisplay); + return false; + } + if (DBG) { + Slogf.d(TAG, "addding %d -> %d to map", displayId, userId); + } + mExtraDisplaysAssignedToUsers.put(displayId, userId); + } + return true; + } + + /** + * See {@link UserManagerInternal#unassignUserFromExtraDisplay(int, int)}. + */ + public boolean unassignUserFromExtraDisplay(@UserIdInt int userId, int displayId) { + if (DBG) { + Slogf.d(TAG, "unassignUserFromExtraDisplay(%d, %d)", userId, displayId); + } + if (!mVisibleBackgroundUsersEnabled) { + Slogf.w(TAG, "unassignUserFromExtraDisplay(%d, %d): called when not supported", + userId, displayId); + return false; + } + synchronized (mLock) { + int assignedUserId = mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL); + if (assignedUserId == USER_NULL) { + Slogf.w(TAG, "unassignUserFromExtraDisplay(%d, %d): not assigned to any user", + userId, displayId); + return false; + } + if (assignedUserId != userId) { + Slogf.w(TAG, "unassignUserFromExtraDisplay(%d, %d): was assigned to user %d", + userId, displayId, assignedUserId); + return false; + } + if (DBG) { + Slogf.d(TAG, "removing %d from map", displayId); + } + mExtraDisplaysAssignedToUsers.delete(displayId); + } + return true; + } + /** * See {@link UserManagerInternal#unassignUserFromDisplayOnStop(int)}. */ @@ -373,7 +485,7 @@ public final class UserVisibilityMediator implements Dumpable { synchronized (mLock) { visibleUsersBefore = getVisibleUsers(); - unassignUserFromDisplayOnStopLocked(userId); + unassignUserFromAllDisplaysOnStopLocked(userId); visibleUsersAfter = getVisibleUsers(); } @@ -381,7 +493,7 @@ public final class UserVisibilityMediator implements Dumpable { } @GuardedBy("mLock") - private void unassignUserFromDisplayOnStopLocked(@UserIdInt int userId) { + private void unassignUserFromAllDisplaysOnStopLocked(@UserIdInt int userId) { if (DBG) { Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, mStartedProfileGroupIds); @@ -395,10 +507,21 @@ public final class UserVisibilityMediator implements Dumpable { return; } if (DBG) { - Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId, - mUsersOnDisplaysMap); + Slogf.d(TAG, "Removing user %d from mUsersOnDisplaysMap (%s)", userId, + mUsersAssignedToDisplayOnStart); + } + mUsersAssignedToDisplayOnStart.delete(userId); + + // Remove extra displays as well + for (int i = mExtraDisplaysAssignedToUsers.size() - 1; i >= 0; i--) { + if (mExtraDisplaysAssignedToUsers.valueAt(i) == userId) { + if (DBG) { + Slogf.d(TAG, "Removing display %d from mExtraDisplaysAssignedToUsers (%s)", + mExtraDisplaysAssignedToUsers.keyAt(i), mExtraDisplaysAssignedToUsers); + } + mExtraDisplaysAssignedToUsers.removeAt(i); + } } - mUsersOnDisplaysMap.delete(userId); } /** @@ -424,7 +547,7 @@ public final class UserVisibilityMediator implements Dumpable { boolean visible; synchronized (mLock) { - visible = mUsersOnDisplaysMap.indexOfKey(userId) >= 0; + visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0; } if (DBG) { Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible); @@ -448,7 +571,12 @@ public final class UserVisibilityMediator implements Dumpable { } synchronized (mLock) { - return mUsersOnDisplaysMap.get(userId, Display.INVALID_DISPLAY) == displayId; + if (mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY) == displayId) { + // User assigned to display on start + return true; + } + // Check for extra assignment + return mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId; } } @@ -465,24 +593,34 @@ public final class UserVisibilityMediator implements Dumpable { } synchronized (mLock) { - return mUsersOnDisplaysMap.get(userId, Display.INVALID_DISPLAY); + return mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY); } } /** * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}. */ - public int getUserAssignedToDisplay(@UserIdInt int displayId) { - if (displayId == Display.DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled) { + public @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId) { + return getUserAssignedToDisplay(displayId, /* returnCurrentUserByDefault= */ true); + } + + /** + * Gets the user explicitly assigned to a display, or the current user when no user is assigned + * to it (and {@code returnCurrentUserByDefault} is {@code true}). + */ + private @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId, + boolean returnCurrentUserByDefault) { + if (returnCurrentUserByDefault + && (displayId == Display.DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled)) { return getCurrentUserId(); } synchronized (mLock) { - for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { - if (mUsersOnDisplaysMap.valueAt(i) != displayId) { + for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { + if (mUsersAssignedToDisplayOnStart.valueAt(i) != displayId) { continue; } - int userId = mUsersOnDisplaysMap.keyAt(i); + int userId = mUsersAssignedToDisplayOnStart.keyAt(i); if (!isStartedProfile(userId)) { return userId; } else if (DBG) { @@ -491,6 +629,13 @@ public final class UserVisibilityMediator implements Dumpable { } } } + if (!returnCurrentUserByDefault) { + if (DBG) { + Slogf.d(TAG, "getUserAssignedToDisplay(%d): no user assigned to display, returning " + + "USER_NULL instead", displayId); + } + return USER_NULL; + } int currentUserId = getCurrentUserId(); if (DBG) { @@ -618,9 +763,11 @@ public final class UserVisibilityMediator implements Dumpable { ipw.print("Supports visible background users on displays: "); ipw.println(mVisibleBackgroundUsersEnabled); - if (mUsersOnDisplaysMap != null) { - dumpSparseIntArray(ipw, mUsersOnDisplaysMap, "user / display", "u", "d"); - } + dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d"); + + dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user", + "d", "u"); + int numberListeners = mListeners.size(); ipw.print("Number of listeners: "); ipw.println(numberListeners); @@ -638,8 +785,14 @@ public final class UserVisibilityMediator implements Dumpable { ipw.decreaseIndent(); } - private static void dumpSparseIntArray(IndentingPrintWriter ipw, SparseIntArray array, + private static void dumpSparseIntArray(IndentingPrintWriter ipw, @Nullable SparseIntArray array, String arrayDescription, String keyName, String valueName) { + if (array == null) { + ipw.print("No "); + ipw.print(arrayDescription); + ipw.println(" mappings"); + return; + } ipw.print("Number of "); ipw.print(arrayDescription); ipw.print(" mappings: "); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java index 88709e164d79f..b9ba780ff685a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java @@ -49,6 +49,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(USER_ID); expectUserIsNotVisibleOnDisplay(USER_ID, INVALID_DISPLAY); @@ -80,6 +81,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(currentUserId, DEFAULT_DISPLAY); expectUserIsVisible(currentUserId); expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY); @@ -110,6 +112,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG_VISIBLE, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(PROFILE_USER_ID); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java index e4664d2c2c469..c59834bea6cae 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java @@ -165,12 +165,16 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @Test public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception { visibleBgUserCannotBeStartedOnDefaultDisplayTest(); + + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); } protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception { @@ -180,8 +184,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); - expectUserIsNotVisibleAtAll(PROFILE_USER_ID); - expectNoDisplayAssignedToUser(PROFILE_USER_ID); + expectUserIsNotVisibleAtAll(USER_ID); + expectNoDisplayAssignedToUser(USER_ID); listener.verify(); } @@ -194,8 +198,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { SECONDARY_DISPLAY_ID); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); - expectUserIsNotVisibleAtAll(PROFILE_USER_ID); - expectNoDisplayAssignedToUser(PROFILE_USER_ID); + expectUserIsNotVisibleAtAll(USER_ID); + expectNoDisplayAssignedToUser(USER_ID); + + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); } @@ -217,6 +224,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_SYSTEM); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); + assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID); + assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, OTHER_SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -256,6 +266,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID); + assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -289,6 +301,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); + assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -305,6 +319,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, + OTHER_SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -320,6 +338,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, + OTHER_SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -336,6 +358,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -351,6 +376,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, + OTHER_SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -481,6 +510,63 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(actualResult).isEqualTo(expectedResult); } + protected void assertBgUserBecomesInvisibleOnStop(@UserIdInt int userId) { + Log.d(TAG, "Stopping user " + userId); + mMediator.unassignUserFromDisplayOnStop(userId); + expectUserIsNotVisibleAtAll(userId); + } + + /** + * Assigns and unassigns the user to / from an extra display, asserting the visibility state in + * between. + * + *

It assumes the user was not visible in the display beforehand. + */ + protected void assertUserCanBeAssignedExtraDisplay(@UserIdInt int userId, int displayId) { + assertUserCanBeAssignedExtraDisplay(userId, displayId, /* unassign= */ true); + } + + protected void assertUserCanBeAssignedExtraDisplay(@UserIdInt int userId, int displayId, + boolean unassign) { + + expectUserIsNotVisibleOnDisplay(userId, displayId); + + Log.d(TAG, "Calling assignUserToExtraDisplay(" + userId + ", " + displayId + ")"); + assertWithMessage("assignUserToExtraDisplay(%s, %s)", userId, displayId) + .that(mMediator.assignUserToExtraDisplay(userId, displayId)) + .isTrue(); + expectUserIsVisibleOnDisplay(userId, displayId); + + if (unassign) { + Log.d(TAG, "Calling unassignUserFromExtraDisplay(" + userId + ", " + displayId + ")"); + assertWithMessage("unassignUserFromExtraDisplay(%s, %s)", userId, displayId) + .that(mMediator.unassignUserFromExtraDisplay(userId, displayId)) + .isTrue(); + expectUserIsNotVisibleOnDisplay(userId, displayId); + } + } + + /** + * Asserts that a user (already visible or not) cannot be assigned to an extra display (and + * hence won't be visible on that display). + */ + protected void assertUserCannotBeAssignedExtraDisplay(@UserIdInt int userId, int displayId) { + expectWithMessage("assignUserToExtraDisplay(%s, %s)", userId, displayId) + .that(mMediator.assignUserToExtraDisplay(userId, displayId)) + .isFalse(); + expectUserIsNotVisibleOnDisplay(userId, displayId); + } + + /** + * Asserts that an invisible user cannot be assigned to an extra display. + */ + protected void assertInvisibleUserCannotBeAssignedExtraDisplay(@UserIdInt int userId, + int displayId) { + assertUserCannotBeAssignedExtraDisplay(userId, displayId); + expectNoDisplayAssignedToUser(userId); + expectInitialCurrentUserAssignedToDisplay(displayId); + } + protected void expectUserIsVisible(@UserIdInt int userId) { expectWithMessage("isUserVisible(%s)", userId) .that(mMediator.isUserVisible(userId)) @@ -534,6 +620,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(mMediator.getDisplayAssignedToUser(userId)).isEqualTo(INVALID_DISPLAY); } + protected void expectUserCannotBeUnassignedFromDisplay(@UserIdInt int userId, int displayId) { + expectWithMessage("unassignUserFromExtraDisplay(%s, %s)", userId, displayId) + .that(mMediator.unassignUserFromExtraDisplay(userId, displayId)).isFalse(); + } + protected void expectUserAssignedToDisplay(int displayId, @UserIdInt int userId) { expectWithMessage("getUserAssignedToDisplay(%s)", displayId) .that(mMediator.getUserAssignedToDisplay(displayId)).isEqualTo(userId); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java index 66d7eb6e603eb..627553bcfa185 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java @@ -52,6 +52,7 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(USER_ID); expectUserIsVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY); @@ -64,7 +65,9 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectUserAssignedToDisplay(INVALID_DISPLAY, USER_ID); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); - expectDisplayAssignedToUser(USER_NULL, INVALID_DISPLAY); + expectNoDisplayAssignedToUser(USER_NULL); + + assertUserCanBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); } @@ -83,6 +86,7 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(currentUserId, DEFAULT_DISPLAY); expectUserIsVisible(currentUserId); expectUserIsVisibleOnDisplay(currentUserId, DEFAULT_DISPLAY); @@ -98,6 +102,8 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectUserIsNotVisibleAtAll(previousCurrentUserId); expectNoDisplayAssignedToUser(previousCurrentUserId); + assertUserCanBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -113,6 +119,7 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG_VISIBLE, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(PROFILE_USER_ID); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); @@ -123,6 +130,8 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectDisplayAssignedToUser(PROFILE_USER_ID, DEFAULT_DISPLAY); expectUserAssignedToDisplay(DEFAULT_DISPLAY, PARENT_USER_ID); + assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -134,6 +143,9 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, DEFAULT_DISPLAY); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -148,6 +160,9 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectUserIsNotVisibleAtAll(USER_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, DEFAULT_DISPLAY); + assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -159,6 +174,7 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG_VISIBLE, SECONDARY_DISPLAY_ID); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + expectUserCannotBeUnassignedFromDisplay(USER_ID, SECONDARY_DISPLAY_ID); expectUserIsVisible(USER_ID); expectUserIsVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID); @@ -169,7 +185,16 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectDisplayAssignedToUser(USER_ID, SECONDARY_DISPLAY_ID); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); - listener.verify(); + assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID); + + // Assign again, without unassigning (to make sure it becomes invisible on stop) + AsyncUserVisibilityListener listener2 = addListenerForEvents(onInvisible(USER_ID)); + assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID, + /* unassign= */ false); + + assertBgUserBecomesInvisibleOnStop(USER_ID); + + listener2.verify(); } @Test @@ -203,6 +228,8 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectNoDisplayAssignedToUser(USER_ID); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, OTHER_USER_ID); + assertUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } @@ -226,7 +253,18 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectDisplayAssignedToUser(USER_ID, OTHER_SECONDARY_DISPLAY_ID); expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, USER_ID); + assertUserCanBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); + + // Assign again, without unassigning (to make sure it becomes invisible on stop) + AsyncUserVisibilityListener listener2 = addListenerForEvents(onInvisible(USER_ID)); + assertUserCanBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID, + /* unassign= */ false); + + assertBgUserBecomesInvisibleOnStop(USER_ID); + + listener2.verify(); } @Test @@ -244,12 +282,14 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, PARENT_USER_ID); + assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + listener.verify(); } + // Conditions below are asserted on other tests, but they're explicitly checked in the 2 + // tests below (which call this method) as well private void currentUserVisibilityWhenNoDisplayIsAssignedTest(@UserIdInt int currentUserId) { - // Conditions below are asserted on other tests, but they're explicitly checked in the 2 - // tests below as well expectUserIsVisible(currentUserId); expectUserIsVisibleOnDisplay(currentUserId, DEFAULT_DISPLAY); expectUserIsNotVisibleOnDisplay(currentUserId, SECONDARY_DISPLAY_ID); @@ -277,4 +317,13 @@ abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase expectUserIsNotVisibleAtAll(INITIAL_CURRENT_USER_ID); expectDisplayAssignedToUser(INITIAL_CURRENT_USER_ID, INVALID_DISPLAY); } + + @Test + public final void testAssignUserToExtraDisplay_invalidDisplays() throws Exception { + expectWithMessage("assignUserToExtraDisplay(%s, %s)", USER_ID, INVALID_DISPLAY) + .that(mMediator.assignUserToExtraDisplay(USER_ID, INVALID_DISPLAY)).isFalse(); + // DEFAULT_DISPLAY is always assigned to the current user + expectWithMessage("assignUserToExtraDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY) + .that(mMediator.assignUserToExtraDisplay(USER_ID, DEFAULT_DISPLAY)).isFalse(); + } }