Loading services/core/java/com/android/server/pm/UserManagerInternal.java +32 −4 Original line number Original line Diff line number Diff line Loading @@ -388,8 +388,8 @@ public abstract class UserManagerInternal { * and the user is {@link UserManager#isUserVisible() visible}. * and the user is {@link UserManager#isUserVisible() visible}. * * * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user * <p><b>NOTE: </b>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 / * is started); for extra unassignments, callers should call {@link * display assignment, we'll need to provide other APIs. * #assignUserToExtraDisplay(int, int)} instead. * * * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to * pass a valid display id. * pass a valid display id. Loading @@ -397,15 +397,43 @@ public abstract class UserManagerInternal { public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId); @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId); /** * Assigns an extra display to the given user, so the user is visible on that display. * * <p>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). * * <p><b>NOTE: </b>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. * Unassigns a user from its current display when it's stopping. * * * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user * <p><b>NOTE: </b>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 / * is stopped); for extra unassignments, callers should call * display assignment, we'll need to provide other APIs. * {@link #unassignUserFromExtraDisplay(int, int)} instead. */ */ public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); /** * Unassigns the extra display from the given user. * * <p>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). * * <p><b>NOTE: </b>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 * Returns {@code true} if the user is visible (as defined by * {@link UserManager#isUserVisible()}. * {@link UserManager#isUserVisible()}. Loading services/core/java/com/android/server/pm/UserManagerService.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -7037,6 +7037,16 @@ public class UserManagerService extends IUserManager.Stub { userStartMode, displayId); 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 @Override public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { mUserVisibilityMediator.unassignUserFromDisplayOnStop(userId); mUserVisibilityMediator.unassignUserFromDisplayOnStop(userId); Loading services/core/java/com/android/server/pm/UserVisibilityMediator.java +176 −23 Original line number Original line Diff line number Diff line Loading @@ -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_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.DEFAULT_DISPLAY; 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_FAILURE; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; Loading Loading @@ -113,7 +114,18 @@ public final class UserVisibilityMediator implements Dumpable { */ */ @Nullable @Nullable @GuardedBy("mLock") @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). * * <p>Only set when {@code mUsersOnSecondaryDisplaysEnabled} is {@code true}. */ @Nullable @GuardedBy("mLock") private final SparseIntArray mExtraDisplaysAssignedToUsers; /** /** * Mapping from each started user to its profile group. * Mapping from each started user to its profile group. Loading @@ -137,7 +149,13 @@ public final class UserVisibilityMediator implements Dumpable { @VisibleForTesting @VisibleForTesting UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) { UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) { mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled; mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled; mUsersOnDisplaysMap = mVisibleBackgroundUsersEnabled ? new SparseIntArray() : null; if (mVisibleBackgroundUsersEnabled) { mUsersAssignedToDisplayOnStart = new SparseIntArray(); mExtraDisplaysAssignedToUsers = new SparseIntArray(); } else { mUsersAssignedToDisplayOnStart = null; mExtraDisplaysAssignedToUsers = null; } mHandler = handler; mHandler = handler; // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices // 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); mStartedProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID); Loading Loading @@ -207,7 +225,7 @@ public final class UserVisibilityMediator implements Dumpable { if (DBG) { if (DBG) { Slogf.d(TAG, "adding user / display mapping (%d -> %d)", userId, displayId); Slogf.d(TAG, "adding user / display mapping (%d -> %d)", userId, displayId); } } mUsersOnDisplaysMap.put(userId, displayId); mUsersAssignedToDisplayOnStart.put(userId, displayId); break; break; case SECONDARY_DISPLAY_MAPPING_NOT_NEEDED: case SECONDARY_DISPLAY_MAPPING_NOT_NEEDED: if (DBG) { if (DBG) { Loading Loading @@ -341,9 +359,9 @@ public final class UserVisibilityMediator implements Dumpable { } } // Check if display is available // Check if display is available for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { int assignedUserId = mUsersOnDisplaysMap.keyAt(i); int assignedUserId = mUsersAssignedToDisplayOnStart.keyAt(i); int assignedDisplayId = mUsersOnDisplaysMap.valueAt(i); int assignedDisplayId = mUsersAssignedToDisplayOnStart.valueAt(i); if (DBG) { if (DBG) { Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d", Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d", i, assignedUserId, assignedDisplayId); i, assignedUserId, assignedDisplayId); Loading @@ -362,6 +380,100 @@ public final class UserVisibilityMediator implements Dumpable { return SECONDARY_DISPLAY_MAPPING_NEEDED; 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)}. * See {@link UserManagerInternal#unassignUserFromDisplayOnStop(int)}. */ */ Loading @@ -373,7 +485,7 @@ public final class UserVisibilityMediator implements Dumpable { synchronized (mLock) { synchronized (mLock) { visibleUsersBefore = getVisibleUsers(); visibleUsersBefore = getVisibleUsers(); unassignUserFromDisplayOnStopLocked(userId); unassignUserFromAllDisplaysOnStopLocked(userId); visibleUsersAfter = getVisibleUsers(); visibleUsersAfter = getVisibleUsers(); } } Loading @@ -381,7 +493,7 @@ public final class UserVisibilityMediator implements Dumpable { } } @GuardedBy("mLock") @GuardedBy("mLock") private void unassignUserFromDisplayOnStopLocked(@UserIdInt int userId) { private void unassignUserFromAllDisplaysOnStopLocked(@UserIdInt int userId) { if (DBG) { if (DBG) { Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, mStartedProfileGroupIds); mStartedProfileGroupIds); Loading @@ -395,10 +507,21 @@ public final class UserVisibilityMediator implements Dumpable { return; return; } } if (DBG) { if (DBG) { Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId, Slogf.d(TAG, "Removing user %d from mUsersOnDisplaysMap (%s)", userId, mUsersOnDisplaysMap); 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); } } /** /** Loading @@ -424,7 +547,7 @@ public final class UserVisibilityMediator implements Dumpable { boolean visible; boolean visible; synchronized (mLock) { synchronized (mLock) { visible = mUsersOnDisplaysMap.indexOfKey(userId) >= 0; visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0; } } if (DBG) { if (DBG) { Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible); Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible); Loading @@ -448,7 +571,12 @@ public final class UserVisibilityMediator implements Dumpable { } } synchronized (mLock) { 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; } } } } Loading @@ -465,24 +593,34 @@ public final class UserVisibilityMediator implements Dumpable { } } synchronized (mLock) { synchronized (mLock) { return mUsersOnDisplaysMap.get(userId, Display.INVALID_DISPLAY); return mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY); } } } } /** /** * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}. * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}. */ */ public int getUserAssignedToDisplay(@UserIdInt int displayId) { public @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId) { if (displayId == Display.DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled) { 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(); return getCurrentUserId(); } } synchronized (mLock) { synchronized (mLock) { for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { if (mUsersOnDisplaysMap.valueAt(i) != displayId) { if (mUsersAssignedToDisplayOnStart.valueAt(i) != displayId) { continue; continue; } } int userId = mUsersOnDisplaysMap.keyAt(i); int userId = mUsersAssignedToDisplayOnStart.keyAt(i); if (!isStartedProfile(userId)) { if (!isStartedProfile(userId)) { return userId; return userId; } else if (DBG) { } else if (DBG) { Loading @@ -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(); int currentUserId = getCurrentUserId(); if (DBG) { if (DBG) { Loading Loading @@ -618,9 +763,11 @@ public final class UserVisibilityMediator implements Dumpable { ipw.print("Supports visible background users on displays: "); ipw.print("Supports visible background users on displays: "); ipw.println(mVisibleBackgroundUsersEnabled); ipw.println(mVisibleBackgroundUsersEnabled); if (mUsersOnDisplaysMap != null) { dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d"); dumpSparseIntArray(ipw, mUsersOnDisplaysMap, "user / display", "u", "d"); } dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user", "d", "u"); int numberListeners = mListeners.size(); int numberListeners = mListeners.size(); ipw.print("Number of listeners: "); ipw.print("Number of listeners: "); ipw.println(numberListeners); ipw.println(numberListeners); Loading @@ -638,8 +785,14 @@ public final class UserVisibilityMediator implements Dumpable { ipw.decreaseIndent(); 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) { 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("Number of "); ipw.print(arrayDescription); ipw.print(arrayDescription); ipw.print(" mappings: "); ipw.print(" mappings: "); Loading services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -49,6 +49,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(USER_ID); expectUserIsVisible(USER_ID); expectUserIsNotVisibleOnDisplay(USER_ID, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(USER_ID, INVALID_DISPLAY); Loading Loading @@ -80,6 +81,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(currentUserId, DEFAULT_DISPLAY); expectUserIsVisible(currentUserId); expectUserIsVisible(currentUserId); expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY); Loading Loading @@ -110,6 +112,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG_VISIBLE, DEFAULT_DISPLAY); BG_VISIBLE, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(PROFILE_USER_ID); expectUserIsVisible(PROFILE_USER_ID); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); Loading services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java +95 −4 Original line number Original line Diff line number Diff line Loading @@ -165,12 +165,16 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_ID); expectNoDisplayAssignedToUser(USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } @Test @Test public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception { public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception { visibleBgUserCannotBeStartedOnDefaultDisplayTest(); visibleBgUserCannotBeStartedOnDefaultDisplayTest(); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); } } protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception { protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception { Loading @@ -180,8 +184,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); expectUserIsNotVisibleAtAll(PROFILE_USER_ID); expectUserIsNotVisibleAtAll(USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(USER_ID); listener.verify(); listener.verify(); } } Loading @@ -194,8 +198,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { SECONDARY_DISPLAY_ID); SECONDARY_DISPLAY_ID); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); expectUserIsNotVisibleAtAll(PROFILE_USER_ID); expectUserIsNotVisibleAtAll(USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(USER_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -217,6 +224,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_SYSTEM); expectNoDisplayAssignedToUser(USER_SYSTEM); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID); assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -256,6 +266,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID); expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID); assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -289,6 +301,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -305,6 +319,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -320,6 +338,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -336,6 +358,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -351,6 +376,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -481,6 +510,63 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(actualResult).isEqualTo(expectedResult); .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. * * <p>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) { protected void expectUserIsVisible(@UserIdInt int userId) { expectWithMessage("isUserVisible(%s)", userId) expectWithMessage("isUserVisible(%s)", userId) .that(mMediator.isUserVisible(userId)) .that(mMediator.isUserVisible(userId)) Loading Loading @@ -534,6 +620,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(mMediator.getDisplayAssignedToUser(userId)).isEqualTo(INVALID_DISPLAY); .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) { protected void expectUserAssignedToDisplay(int displayId, @UserIdInt int userId) { expectWithMessage("getUserAssignedToDisplay(%s)", displayId) expectWithMessage("getUserAssignedToDisplay(%s)", displayId) .that(mMediator.getUserAssignedToDisplay(displayId)).isEqualTo(userId); .that(mMediator.getUserAssignedToDisplay(displayId)).isEqualTo(userId); Loading Loading
services/core/java/com/android/server/pm/UserManagerInternal.java +32 −4 Original line number Original line Diff line number Diff line Loading @@ -388,8 +388,8 @@ public abstract class UserManagerInternal { * and the user is {@link UserManager#isUserVisible() visible}. * and the user is {@link UserManager#isUserVisible() visible}. * * * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user * <p><b>NOTE: </b>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 / * is started); for extra unassignments, callers should call {@link * display assignment, we'll need to provide other APIs. * #assignUserToExtraDisplay(int, int)} instead. * * * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to * pass a valid display id. * pass a valid display id. Loading @@ -397,15 +397,43 @@ public abstract class UserManagerInternal { public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId); @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId); /** * Assigns an extra display to the given user, so the user is visible on that display. * * <p>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). * * <p><b>NOTE: </b>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. * Unassigns a user from its current display when it's stopping. * * * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user * <p><b>NOTE: </b>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 / * is stopped); for extra unassignments, callers should call * display assignment, we'll need to provide other APIs. * {@link #unassignUserFromExtraDisplay(int, int)} instead. */ */ public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); /** * Unassigns the extra display from the given user. * * <p>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). * * <p><b>NOTE: </b>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 * Returns {@code true} if the user is visible (as defined by * {@link UserManager#isUserVisible()}. * {@link UserManager#isUserVisible()}. Loading
services/core/java/com/android/server/pm/UserManagerService.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -7037,6 +7037,16 @@ public class UserManagerService extends IUserManager.Stub { userStartMode, displayId); 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 @Override public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { mUserVisibilityMediator.unassignUserFromDisplayOnStop(userId); mUserVisibilityMediator.unassignUserFromDisplayOnStop(userId); Loading
services/core/java/com/android/server/pm/UserVisibilityMediator.java +176 −23 Original line number Original line Diff line number Diff line Loading @@ -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_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.DEFAULT_DISPLAY; 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_FAILURE; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; Loading Loading @@ -113,7 +114,18 @@ public final class UserVisibilityMediator implements Dumpable { */ */ @Nullable @Nullable @GuardedBy("mLock") @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). * * <p>Only set when {@code mUsersOnSecondaryDisplaysEnabled} is {@code true}. */ @Nullable @GuardedBy("mLock") private final SparseIntArray mExtraDisplaysAssignedToUsers; /** /** * Mapping from each started user to its profile group. * Mapping from each started user to its profile group. Loading @@ -137,7 +149,13 @@ public final class UserVisibilityMediator implements Dumpable { @VisibleForTesting @VisibleForTesting UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) { UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) { mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled; mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled; mUsersOnDisplaysMap = mVisibleBackgroundUsersEnabled ? new SparseIntArray() : null; if (mVisibleBackgroundUsersEnabled) { mUsersAssignedToDisplayOnStart = new SparseIntArray(); mExtraDisplaysAssignedToUsers = new SparseIntArray(); } else { mUsersAssignedToDisplayOnStart = null; mExtraDisplaysAssignedToUsers = null; } mHandler = handler; mHandler = handler; // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices // 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); mStartedProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID); Loading Loading @@ -207,7 +225,7 @@ public final class UserVisibilityMediator implements Dumpable { if (DBG) { if (DBG) { Slogf.d(TAG, "adding user / display mapping (%d -> %d)", userId, displayId); Slogf.d(TAG, "adding user / display mapping (%d -> %d)", userId, displayId); } } mUsersOnDisplaysMap.put(userId, displayId); mUsersAssignedToDisplayOnStart.put(userId, displayId); break; break; case SECONDARY_DISPLAY_MAPPING_NOT_NEEDED: case SECONDARY_DISPLAY_MAPPING_NOT_NEEDED: if (DBG) { if (DBG) { Loading Loading @@ -341,9 +359,9 @@ public final class UserVisibilityMediator implements Dumpable { } } // Check if display is available // Check if display is available for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { int assignedUserId = mUsersOnDisplaysMap.keyAt(i); int assignedUserId = mUsersAssignedToDisplayOnStart.keyAt(i); int assignedDisplayId = mUsersOnDisplaysMap.valueAt(i); int assignedDisplayId = mUsersAssignedToDisplayOnStart.valueAt(i); if (DBG) { if (DBG) { Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d", Slogf.d(TAG, "%d: assignedUserId=%d, assignedDisplayId=%d", i, assignedUserId, assignedDisplayId); i, assignedUserId, assignedDisplayId); Loading @@ -362,6 +380,100 @@ public final class UserVisibilityMediator implements Dumpable { return SECONDARY_DISPLAY_MAPPING_NEEDED; 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)}. * See {@link UserManagerInternal#unassignUserFromDisplayOnStop(int)}. */ */ Loading @@ -373,7 +485,7 @@ public final class UserVisibilityMediator implements Dumpable { synchronized (mLock) { synchronized (mLock) { visibleUsersBefore = getVisibleUsers(); visibleUsersBefore = getVisibleUsers(); unassignUserFromDisplayOnStopLocked(userId); unassignUserFromAllDisplaysOnStopLocked(userId); visibleUsersAfter = getVisibleUsers(); visibleUsersAfter = getVisibleUsers(); } } Loading @@ -381,7 +493,7 @@ public final class UserVisibilityMediator implements Dumpable { } } @GuardedBy("mLock") @GuardedBy("mLock") private void unassignUserFromDisplayOnStopLocked(@UserIdInt int userId) { private void unassignUserFromAllDisplaysOnStopLocked(@UserIdInt int userId) { if (DBG) { if (DBG) { Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, mStartedProfileGroupIds); mStartedProfileGroupIds); Loading @@ -395,10 +507,21 @@ public final class UserVisibilityMediator implements Dumpable { return; return; } } if (DBG) { if (DBG) { Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId, Slogf.d(TAG, "Removing user %d from mUsersOnDisplaysMap (%s)", userId, mUsersOnDisplaysMap); 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); } } /** /** Loading @@ -424,7 +547,7 @@ public final class UserVisibilityMediator implements Dumpable { boolean visible; boolean visible; synchronized (mLock) { synchronized (mLock) { visible = mUsersOnDisplaysMap.indexOfKey(userId) >= 0; visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0; } } if (DBG) { if (DBG) { Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible); Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible); Loading @@ -448,7 +571,12 @@ public final class UserVisibilityMediator implements Dumpable { } } synchronized (mLock) { 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; } } } } Loading @@ -465,24 +593,34 @@ public final class UserVisibilityMediator implements Dumpable { } } synchronized (mLock) { synchronized (mLock) { return mUsersOnDisplaysMap.get(userId, Display.INVALID_DISPLAY); return mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY); } } } } /** /** * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}. * See {@link UserManagerInternal#getUserAssignedToDisplay(int)}. */ */ public int getUserAssignedToDisplay(@UserIdInt int displayId) { public @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId) { if (displayId == Display.DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled) { 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(); return getCurrentUserId(); } } synchronized (mLock) { synchronized (mLock) { for (int i = 0; i < mUsersOnDisplaysMap.size(); i++) { for (int i = 0; i < mUsersAssignedToDisplayOnStart.size(); i++) { if (mUsersOnDisplaysMap.valueAt(i) != displayId) { if (mUsersAssignedToDisplayOnStart.valueAt(i) != displayId) { continue; continue; } } int userId = mUsersOnDisplaysMap.keyAt(i); int userId = mUsersAssignedToDisplayOnStart.keyAt(i); if (!isStartedProfile(userId)) { if (!isStartedProfile(userId)) { return userId; return userId; } else if (DBG) { } else if (DBG) { Loading @@ -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(); int currentUserId = getCurrentUserId(); if (DBG) { if (DBG) { Loading Loading @@ -618,9 +763,11 @@ public final class UserVisibilityMediator implements Dumpable { ipw.print("Supports visible background users on displays: "); ipw.print("Supports visible background users on displays: "); ipw.println(mVisibleBackgroundUsersEnabled); ipw.println(mVisibleBackgroundUsersEnabled); if (mUsersOnDisplaysMap != null) { dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d"); dumpSparseIntArray(ipw, mUsersOnDisplaysMap, "user / display", "u", "d"); } dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user", "d", "u"); int numberListeners = mListeners.size(); int numberListeners = mListeners.size(); ipw.print("Number of listeners: "); ipw.print("Number of listeners: "); ipw.println(numberListeners); ipw.println(numberListeners); Loading @@ -638,8 +785,14 @@ public final class UserVisibilityMediator implements Dumpable { ipw.decreaseIndent(); 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) { 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("Number of "); ipw.print(arrayDescription); ipw.print(arrayDescription); ipw.print(" mappings: "); ipw.print(" mappings: "); Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -49,6 +49,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(USER_ID); expectUserIsVisible(USER_ID); expectUserIsNotVisibleOnDisplay(USER_ID, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(USER_ID, INVALID_DISPLAY); Loading Loading @@ -80,6 +81,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId, FG, DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(currentUserId, DEFAULT_DISPLAY); expectUserIsVisible(currentUserId); expectUserIsVisible(currentUserId); expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY); Loading Loading @@ -110,6 +112,7 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG_VISIBLE, DEFAULT_DISPLAY); BG_VISIBLE, DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); expectUserIsVisible(PROFILE_USER_ID); expectUserIsVisible(PROFILE_USER_ID); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY); Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java +95 −4 Original line number Original line Diff line number Diff line Loading @@ -165,12 +165,16 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_ID); expectNoDisplayAssignedToUser(USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } @Test @Test public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception { public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception { visibleBgUserCannotBeStartedOnDefaultDisplayTest(); visibleBgUserCannotBeStartedOnDefaultDisplayTest(); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); } } protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception { protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception { Loading @@ -180,8 +184,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { DEFAULT_DISPLAY); DEFAULT_DISPLAY); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); expectUserIsNotVisibleAtAll(PROFILE_USER_ID); expectUserIsNotVisibleAtAll(USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(USER_ID); listener.verify(); listener.verify(); } } Loading @@ -194,8 +198,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { SECONDARY_DISPLAY_ID); SECONDARY_DISPLAY_ID); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); expectUserIsNotVisibleAtAll(PROFILE_USER_ID); expectUserIsNotVisibleAtAll(USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(USER_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -217,6 +224,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(USER_SYSTEM); expectNoDisplayAssignedToUser(USER_SYSTEM); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); expectUserAssignedToDisplay(SECONDARY_DISPLAY_ID, USER_ID); assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID); assertUserCannotBeAssignedExtraDisplay(USER_SYSTEM, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -256,6 +266,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID); expectUserAssignedToDisplay(DEFAULT_DISPLAY, OTHER_USER_ID); assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -289,6 +301,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -305,6 +319,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -320,6 +338,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -336,6 +358,9 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading @@ -351,6 +376,10 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectNoDisplayAssignedToUser(PROFILE_USER_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); expectInitialCurrentUserAssignedToDisplay(SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID); listener.verify(); listener.verify(); } } Loading Loading @@ -481,6 +510,63 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(actualResult).isEqualTo(expectedResult); .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. * * <p>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) { protected void expectUserIsVisible(@UserIdInt int userId) { expectWithMessage("isUserVisible(%s)", userId) expectWithMessage("isUserVisible(%s)", userId) .that(mMediator.isUserVisible(userId)) .that(mMediator.isUserVisible(userId)) Loading Loading @@ -534,6 +620,11 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .that(mMediator.getDisplayAssignedToUser(userId)).isEqualTo(INVALID_DISPLAY); .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) { protected void expectUserAssignedToDisplay(int displayId, @UserIdInt int userId) { expectWithMessage("getUserAssignedToDisplay(%s)", displayId) expectWithMessage("getUserAssignedToDisplay(%s)", displayId) .that(mMediator.getUserAssignedToDisplay(displayId)).isEqualTo(userId); .that(mMediator.getUserAssignedToDisplay(displayId)).isEqualTo(userId); Loading