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 8094e7fd17700..a966d986c566f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -7037,6 +7037,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(); + } }