diff options
| author | 2022-10-28 11:06:49 -0700 | |
|---|---|---|
| committer | 2022-11-14 10:53:47 -0800 | |
| commit | d16bc3eea37fb20ea5114a2b4307c5ee5cf87ca5 (patch) | |
| tree | fd02c8ccde579299f9b3228a7fbda44f8abb10f2 | |
| parent | 90e81227d9857041b6fdbc8d6ed411504bcf3602 (diff) | |
UserVisibilityMediator refactoring, step 2.
The previous refactoring introduced a startUser() on
UserVisibilityMediator and changed UserManagerInternal to call both
startUser() and assignUserToDisplay(); this CL "merges" these two
methods into just startUser(), and changed UserController.startUser()
workflow so the logic of whether the user is visible is handled by
UserVisibilityMediator.
Test: atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest UserVisibilityMediatorMUMDTest UserVisibilityMediatorSUSDTest UserControllerTest # unit
Test: atest CtsMultiUserTestCases:android.multiuser.cts.MultipleUsersOnMultipleDisplaysTest # CTS
Test: adb shell dumpsys user --visibility-mediator
Bug: 244644281
Change-Id: Ide7b62c1ab3bb119dde7dc2790919ad3c864b3e9
8 files changed, 312 insertions, 199 deletions
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 92133274c0cc..4d86140816ea 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1090,7 +1090,7 @@ class UserController implements Handler.Callback { // TODO(b/239982558): for now we're just updating the user's visibility, but most likely // we'll need to remove this call and handle that as part of the user state workflow // instead. - userManagerInternal.unassignUserFromDisplay(userId); + userManagerInternal.unassignUserFromDisplayOnStop(userId); final boolean visibilityChanged; boolean visibleBefore; @@ -1650,13 +1650,30 @@ class UserController implements Handler.Callback { return false; } - if (!userInfo.preCreated) { - // TODO(b/244644281): UMI should return whether the user is visible. And if fails, - // the user should not be in the mediator's started users structure - mInjector.getUserManagerInternal().assignUserToDisplay(userId, - userInfo.profileGroupId, foreground, displayId); + t.traceBegin("assignUserToDisplayOnStart"); + int result = mInjector.getUserManagerInternal().assignUserToDisplayOnStart(userId, + userInfo.profileGroupId, foreground, displayId); + t.traceEnd(); + + boolean visible; + switch (result) { + case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE: + visible = true; + break; + case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE: + visible = false; + break; + default: + Slogf.wtf(TAG, "Wrong result from assignUserToDisplayOnStart(): %d", result); + // Fall through + case UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE: + Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s", + (foreground ? "fg" : "bg"), userId, displayId, + UserManagerInternal.userAssignmentResultToString(result)); + return false; } + // TODO(b/239982558): might need something similar for bg users on secondary display if (foreground && isUserSwitchUiEnabled()) { t.traceBegin("startFreezingScreen"); @@ -1751,19 +1768,6 @@ class UserController implements Handler.Callback { } t.traceEnd(); - // Need to call UM when user is on background, as there are some cases where the user - // cannot be started in background on a secondary display (for example, if user is a - // profile). - // TODO(b/253103846): it's also explicitly checking if the user is the USER_SYSTEM, as - // the UM call would return true during boot (when CarService / BootUserInitializer - // calls AM.startUserInBackground() because the system user is still the current user. - // TODO(b/244644281): another fragility of this check is that it must wait to call - // UMI.isUserVisible() until the user state is check, as that method checks if the - // profile of the current user is started. We should fix that dependency so the logic - // belongs to just one place (like UserDisplayAssigner) - boolean visible = foreground - || userId != UserHandle.USER_SYSTEM - && mInjector.getUserManagerInternal().isUserVisible(userId); if (visible) { synchronized (mLock) { addVisibleUserLocked(userId); @@ -1816,8 +1820,8 @@ class UserController implements Handler.Callback { // user that was started in the background before), so it's necessary to explicitly // notify the services (while when the user starts from BOOTING, USER_START_MSG // takes care of that. - mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG, userId, - visible ? 1 : 0)); + mHandler.sendMessage( + mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG, userId, 1)); } t.traceBegin("sendMessages"); diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index 91558308e305..9dafcceefdd0 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -25,6 +25,7 @@ import android.content.pm.UserProperties; import android.graphics.Bitmap; import android.os.Bundle; import android.os.UserManager; +import android.util.DebugUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -46,6 +47,18 @@ public abstract class UserManagerInternal { public @interface OwnerType { } + public static final int USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE = 1; + public static final int USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE = 2; + public static final int USER_ASSIGNMENT_RESULT_FAILURE = -1; + + private static final String PREFIX_USER_ASSIGNMENT_RESULT = "USER_ASSIGNMENT_RESULT"; + @IntDef(flag = false, prefix = {PREFIX_USER_ASSIGNMENT_RESULT}, value = { + USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE, + USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE, + USER_ASSIGNMENT_RESULT_FAILURE + }) + public @interface UserAssignmentResult {} + public interface UserRestrictionsListener { /** * Called when a user restriction changes. @@ -343,34 +356,28 @@ public abstract class UserManagerInternal { public abstract @Nullable UserProperties getUserProperties(@UserIdInt int userId); /** - * Assigns a user to a display. - * - * <p>On most devices this call will be a no-op, but it will be used on devices that support - * multiple users on multiple displays (like automotives with passenger displays). + * Assigns a user to a display when it's starting, returning whether the assignment succeeded + * 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 - * is started) + * is started). If other clients (like {@code CarService} need to explicitly change the user / + * display assignment, we'll need to provide other APIs. * * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to - * check it. In fact, one of the intended clients for this method is - * {@code DisplayManagerService}, which will call it when a virtual display is created (another - * client is {@code UserController}, which will call it when a user is started). + * pass a valid display id. */ - // TODO(b/244644281): rename to assignUserToDisplayOnStart() and make sure it's called on boot - // as well - public abstract void assignUserToDisplay(@UserIdInt int userId, @UserIdInt int profileGroupId, + public abstract @UserAssignmentResult int assignUserToDisplayOnStart(@UserIdInt int userId, + @UserIdInt int profileGroupId, boolean foreground, int displayId); /** - * Unassigns a user from its current display. - * - * <p>On most devices this call will be a no-op, but it will be used on devices that support - * multiple users on multiple displays (like automotives with passenger displays). + * 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 - * is stopped). + * is stopped). If other clients (like {@code CarService} need to explicitly change the user / + * display assignment, we'll need to provide other APIs. */ - public abstract void unassignUserFromDisplay(@UserIdInt int userId); + public abstract void unassignUserFromDisplayOnStop(@UserIdInt int userId); /** * Returns {@code true} if the user is visible (as defined by @@ -413,6 +420,15 @@ public abstract class UserManagerInternal { */ public abstract @UserIdInt int getUserAssignedToDisplay(int displayId); + /** + * Gets the user-friendly representation of the {@code result} of a + * {@link #assignUserToDisplayOnStart(int, int, boolean, int)} call. + */ + public static String userAssignmentResultToString(@UserAssignmentResult int result) { + return DebugUtils.constantToString(UserManagerInternal.class, PREFIX_USER_ASSIGNMENT_RESULT, + result); + } + /** Adds a {@link UserVisibilityListener}. */ public abstract void addUserVisibilityListener(UserVisibilityListener listener); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d25566980fbb..44b5ba242f91 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -6799,15 +6799,14 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void assignUserToDisplay(@UserIdInt int userId, @UserIdInt int profileGroupId, + public int assignUserToDisplayOnStart(@UserIdInt int userId, @UserIdInt int profileGroupId, boolean foreground, int displayId) { - mUserVisibilityMediator.startUser(userId, profileGroupId, foreground, displayId); - mUserVisibilityMediator.assignUserToDisplay(userId, profileGroupId, displayId); + return mUserVisibilityMediator.startUser(userId, profileGroupId, foreground, + displayId); } @Override - public void unassignUserFromDisplay(@UserIdInt int userId) { - mUserVisibilityMediator.unassignUserFromDisplay(userId); + public void unassignUserFromDisplayOnStop(@UserIdInt int userId) { mUserVisibilityMediator.stopUser(userId); } diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java index bd81062b0ff1..cbf7dfe77ca6 100644 --- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java +++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java @@ -20,12 +20,15 @@ import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.DEFAULT_DISPLAY; -import android.annotation.IntDef; +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_VISIBLE; +import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString; + import android.annotation.Nullable; import android.annotation.UserIdInt; import android.os.UserHandle; import android.os.UserManager; -import android.util.DebugUtils; import android.util.Dumpable; import android.util.IndentingPrintWriter; import android.util.SparseIntArray; @@ -34,6 +37,7 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.server.pm.UserManagerInternal.UserAssignmentResult; import com.android.server.utils.Slogf; import java.io.PrintWriter; @@ -52,23 +56,10 @@ public final class UserVisibilityMediator implements Dumpable { private static final String TAG = UserVisibilityMediator.class.getSimpleName(); - private static final String PREFIX_START_USER_RESULT = "START_USER_"; - // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices @VisibleForTesting static final int INITIAL_CURRENT_USER_ID = USER_SYSTEM; - public static final int START_USER_RESULT_SUCCESS_VISIBLE = 1; - public static final int START_USER_RESULT_SUCCESS_INVISIBLE = 2; - public static final int START_USER_RESULT_FAILURE = -1; - - @IntDef(flag = false, prefix = {PREFIX_START_USER_RESULT}, value = { - START_USER_RESULT_SUCCESS_VISIBLE, - START_USER_RESULT_SUCCESS_INVISIBLE, - START_USER_RESULT_FAILURE - }) - public @interface StartUserResult {} - private final Object mLock = new Object(); private final boolean mUsersOnSecondaryDisplaysEnabled; @@ -97,10 +88,37 @@ public final class UserVisibilityMediator implements Dumpable { } /** - * TODO(b/244644281): merge with assignUserToDisplay() or add javadoc. + * See {@link UserManagerInternal#assignUserToDisplayOnStart(int, int, boolean, int)}. */ - public @StartUserResult int startUser(@UserIdInt int userId, @UserIdInt int profileGroupId, + public @UserAssignmentResult int startUser(@UserIdInt int userId, @UserIdInt int profileGroupId, boolean foreground, int displayId) { + // TODO(b/244644281): this method need to perform 4 actions: + // + // 1. Check if the user can be started given the provided arguments + // 2. If it can, decide whether it's visible or not (which is the return value) + // 3. Update the current user / profiles state + // 4. Update the users on secondary display state (if applicable) + // + // Ideally, they should be done "atomically" (i.e, only changing state while holding the + // mLock), but the initial implementation is just calling the existing methods, as the + // focus is to change the UserController startUser() workflow (so it relies on this class + // for the logic above). + // + // The next CL will refactor it (and the unit tests) to achieve that atomicity. + int result = startOnly(userId, profileGroupId, foreground, displayId); + if (result != USER_ASSIGNMENT_RESULT_FAILURE) { + assignUserToDisplay(userId, profileGroupId, displayId); + } + return result; + } + + /** + * @deprecated - see comment inside {@link #startUser(int, int, boolean, int)} + */ + @Deprecated + @VisibleForTesting + @UserAssignmentResult int startOnly(@UserIdInt int userId, + @UserIdInt int profileGroupId, boolean foreground, int displayId) { int actualProfileGroupId = profileGroupId == NO_PROFILE_GROUP_ID ? userId : profileGroupId; @@ -111,7 +129,7 @@ public final class UserVisibilityMediator implements Dumpable { if (foreground && displayId != DEFAULT_DISPLAY) { Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start foreground user on " + "secondary display", userId, actualProfileGroupId, foreground, displayId); - return START_USER_RESULT_FAILURE; + return USER_ASSIGNMENT_RESULT_FAILURE; } int visibility; @@ -121,13 +139,12 @@ public final class UserVisibilityMediator implements Dumpable { Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user on " + "secondary display", userId, actualProfileGroupId, foreground, displayId); - return START_USER_RESULT_FAILURE; + return USER_ASSIGNMENT_RESULT_FAILURE; } if (foreground) { Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user in " - + "foreground", userId, actualProfileGroupId, foreground, - displayId); - return START_USER_RESULT_FAILURE; + + "foreground", userId, actualProfileGroupId, foreground, displayId); + return USER_ASSIGNMENT_RESULT_FAILURE; } else { boolean isParentRunning = mStartedProfileGroupIds .get(actualProfileGroupId) == actualProfileGroupId; @@ -135,18 +152,18 @@ public final class UserVisibilityMediator implements Dumpable { Slogf.d(TAG, "profile parent running: %b", isParentRunning); } visibility = isParentRunning - ? START_USER_RESULT_SUCCESS_VISIBLE - : START_USER_RESULT_SUCCESS_INVISIBLE; + ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE + : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; } } else if (foreground) { mCurrentUserId = userId; - visibility = START_USER_RESULT_SUCCESS_VISIBLE; + visibility = USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE; } else { - visibility = START_USER_RESULT_SUCCESS_INVISIBLE; + visibility = USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; } if (DBG) { Slogf.d(TAG, "adding user / profile mapping (%d -> %d) and returning %s", - userId, actualProfileGroupId, startUserResultToString(visibility)); + userId, actualProfileGroupId, userAssignmentResultToString(visibility)); } mStartedProfileGroupIds.put(userId, actualProfileGroupId); } @@ -154,21 +171,11 @@ public final class UserVisibilityMediator implements Dumpable { } /** - * TODO(b/244644281): merge with unassignUserFromDisplay() or add javadoc (and unit tests) - */ - public void stopUser(@UserIdInt int userId) { - if (DBG) { - Slogf.d(TAG, "stopUser(%d)", userId); - } - synchronized (mLock) { - mStartedProfileGroupIds.delete(userId); - } - } - - /** - * See {@link UserManagerInternal#assignUserToDisplay(int, int)}. + * @deprecated - see comment inside {@link #startUser(int, int, boolean, int)} */ - public void assignUserToDisplay(int userId, int profileGroupId, int displayId) { + @Deprecated + @VisibleForTesting + void assignUserToDisplay(int userId, int profileGroupId, int displayId) { if (DBG) { Slogf.d(TAG, "assignUserToDisplay(%d, %d): mUsersOnSecondaryDisplaysEnabled=%b", userId, displayId, mUsersOnSecondaryDisplaysEnabled); @@ -246,22 +253,25 @@ public final class UserVisibilityMediator implements Dumpable { } /** - * See {@link UserManagerInternal#unassignUserFromDisplay(int)}. + * See {@link UserManagerInternal#unassignUserFromDisplayOnStop(int)}. */ - public void unassignUserFromDisplay(int userId) { + public void stopUser(int userId) { if (DBG) { - Slogf.d(TAG, "unassignUserFromDisplay(%d)", userId); + Slogf.d(TAG, "stopUser(%d)", userId); } - if (!mUsersOnSecondaryDisplaysEnabled) { - // Don't need to do anything because methods (such as isUserVisible()) already know - // that the current user (and their profiles) is assigned to the default display. + synchronized (mLock) { if (DBG) { - Slogf.d(TAG, "ignoring when device doesn't support MUMD"); + Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId, + mStartedProfileGroupIds); } - return; - } + mStartedProfileGroupIds.delete(userId); - synchronized (mLock) { + if (!mUsersOnSecondaryDisplaysEnabled) { + // Don't need to do update mUsersOnSecondaryDisplays because methods (such as + // isUserVisible()) already know that the current user (and their profiles) is + // assigned to the default display. + return; + } if (DBG) { Slogf.d(TAG, "Removing %d from mUsersOnSecondaryDisplays (%s)", userId, mUsersOnSecondaryDisplays); @@ -395,33 +405,40 @@ public final class UserVisibilityMediator implements Dumpable { ipw.print("Current user id: "); ipw.println(mCurrentUserId); - ipw.print("Number of started user / profile group mappings: "); - ipw.println(mStartedProfileGroupIds.size()); - if (mStartedProfileGroupIds.size() > 0) { - ipw.increaseIndent(); - for (int i = 0; i < mStartedProfileGroupIds.size(); i++) { - ipw.print("User #"); - ipw.print(mStartedProfileGroupIds.keyAt(i)); - ipw.print(" -> profile #"); - ipw.println(mStartedProfileGroupIds.valueAt(i)); - } - ipw.decreaseIndent(); - } + dumpIntArray(ipw, mStartedProfileGroupIds, "started user / profile group", "u", "pg"); - ipw.print("Supports users on secondary displays: "); + ipw.print("Supports background users on secondary displays: "); ipw.println(mUsersOnSecondaryDisplaysEnabled); if (mUsersOnSecondaryDisplaysEnabled) { - ipw.print("Users on secondary displays: "); - synchronized (mLock) { - ipw.println(mUsersOnSecondaryDisplays); - } + dumpIntArray(ipw, mUsersOnSecondaryDisplays, "background user / secondary display", + "u", "d"); } } ipw.decreaseIndent(); } + private static void dumpIntArray(IndentingPrintWriter ipw, SparseIntArray array, + String arrayDescription, String keyName, String valueName) { + ipw.print("Number of "); + ipw.print(arrayDescription); + ipw.print(" mappings: "); + ipw.println(array.size()); + if (array.size() <= 0) { + return; + } + ipw.increaseIndent(); + for (int i = 0; i < array.size(); i++) { + ipw.print(keyName); ipw.print(':'); + ipw.print(array.keyAt(i)); + ipw.print(" -> "); + ipw.print(valueName); ipw.print(':'); + ipw.println(array.valueAt(i)); + } + ipw.decreaseIndent(); + } + @Override public void dump(PrintWriter pw, String[] args) { if (pw instanceof IndentingPrintWriter) { @@ -445,14 +462,6 @@ public final class UserVisibilityMediator implements Dumpable { return map; } - /** - * Gets the user-friendly representation of the {@code result}. - */ - public static String startUserResultToString(@StartUserResult int result) { - return DebugUtils.constantToString(UserVisibilityMediator.class, PREFIX_START_USER_RESULT, - result); - } - // TODO(b/244644281): methods below are needed because some APIs use the current users (full and // profiles) state to decide whether a user is visible or not. If we decide to always store that // info into intermediate maps, we should remove them. @@ -483,6 +492,14 @@ public final class UserVisibilityMediator implements Dumpable { } @VisibleForTesting + boolean isStartedUser(@UserIdInt int userId) { + synchronized (mLock) { + return mStartedProfileGroupIds.get(userId, + INITIAL_CURRENT_USER_ID) != INITIAL_CURRENT_USER_ID; + } + } + + @VisibleForTesting boolean isStartedProfile(@UserIdInt int userId) { int profileGroupId; synchronized (mLock) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java index 923c3e385b5e..9be370fe3045 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java @@ -153,14 +153,8 @@ public final class UserVisibilityMediatorMUMDTest extends UserVisibilityMediator assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID); } - @Test - public void testUnassignUserFromDisplay() { - testAssignUserToDisplay_displayAvailable(); - - mMediator.unassignUserFromDisplay(USER_ID); - - assertNoUserAssignedToDisplay(); - } + // TODO(b/244644281): when start & assign are merged, rename tests above and also call + // stopUserAndAssertState() at the end of them @Test public void testIsUserVisible_bgUserOnSecondaryDisplay() { 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 7af5f5d6b2fe..7abdd9e7bbaf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java @@ -15,8 +15,6 @@ */ package com.android.server.pm; -import static android.os.UserHandle.USER_SYSTEM; - import static org.junit.Assert.assertThrows; import org.junit.Test; @@ -34,6 +32,9 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator super(/* usersOnSecondaryDisplaysEnabled= */ false); } + // TODO(b/244644281): when start & assign are merged, rename tests below and also call + // stopUserAndAssertState() at the end of them + @Test public void testAssignUserToDisplay_otherDisplay_currentUser() { mockCurrentUser(USER_ID); @@ -59,15 +60,4 @@ public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediator assertThrows(UnsupportedOperationException.class, () -> mMediator .assignUserToDisplay(PROFILE_USER_ID, PARENT_USER_ID, SECONDARY_DISPLAY_ID)); } - - @Test - public void testUnassignUserFromDisplay_ignored() { - mockCurrentUser(USER_ID); - - mMediator.unassignUserFromDisplay(USER_SYSTEM); - mMediator.unassignUserFromDisplay(USER_ID); - mMediator.unassignUserFromDisplay(OTHER_USER_ID); - - assertNoUserAssignedToDisplay(); - } } 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 7b20092b503a..e8be97db717d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java @@ -20,11 +20,11 @@ import static android.os.UserHandle.USER_NULL; 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; +import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE; +import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString; import static com.android.server.pm.UserVisibilityMediator.INITIAL_CURRENT_USER_ID; -import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_FAILURE; -import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_SUCCESS_INVISIBLE; -import static com.android.server.pm.UserVisibilityMediator.START_USER_RESULT_SUCCESS_VISIBLE; -import static com.android.server.pm.UserVisibilityMediator.startUserResultToString; import static com.google.common.truth.Truth.assertWithMessage; @@ -100,79 +100,92 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { @Test public final void testStartUser_currentUser() { - int result = mMediator.startUser(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); - assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE); + int result = mMediator.startOnly(USER_ID, USER_ID, FG, DEFAULT_DISPLAY); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertCurrentUser(USER_ID); assertIsCurrentUserOrRunningProfileOfCurrentUser(USER_ID); assertStartedProfileGroupIdOf(USER_ID, USER_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_currentUserSecondaryDisplay() { - int result = mMediator.startUser(USER_ID, USER_ID, FG, SECONDARY_DISPLAY_ID); - assertStartUserResult(result, START_USER_RESULT_FAILURE); + int result = mMediator.startOnly(USER_ID, USER_ID, FG, SECONDARY_DISPLAY_ID); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertCurrentUser(INITIAL_CURRENT_USER_ID); assertIsNotCurrentUserOrRunningProfileOfCurrentUser(USER_ID); assertStartedProfileGroupIdOf(USER_ID, NO_PROFILE_GROUP_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_profileBg_parentStarted() { mockCurrentUser(PARENT_USER_ID); - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); - assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE); + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertCurrentUser(PARENT_USER_ID); assertIsCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID); assertStartedProfileGroupIdOf(PROFILE_USER_ID, PARENT_USER_ID); - assertIsStartedProfile(PROFILE_USER_ID); + assertProfileIsStarted(PROFILE_USER_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_profileBg_parentNotStarted() { - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); - assertStartUserResult(result, START_USER_RESULT_SUCCESS_INVISIBLE); + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE); assertCurrentUser(INITIAL_CURRENT_USER_ID); assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID); assertStartedProfileGroupIdOf(PROFILE_USER_ID, PARENT_USER_ID); - assertIsStartedProfile(PROFILE_USER_ID); + assertProfileIsStarted(PROFILE_USER_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_profileBg_secondaryDisplay() { - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, SECONDARY_DISPLAY_ID); - assertStartUserResult(result, START_USER_RESULT_FAILURE); + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, BG, SECONDARY_DISPLAY_ID); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertCurrentUser(INITIAL_CURRENT_USER_ID); assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_profileFg() { - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, FG, DEFAULT_DISPLAY); - assertStartUserResult(result, START_USER_RESULT_FAILURE); + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, FG, DEFAULT_DISPLAY); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertCurrentUser(INITIAL_CURRENT_USER_ID); assertIsNotCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID); - assertStartedProfileGroupIdOf(PROFILE_USER_ID, NO_PROFILE_GROUP_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testStartUser_profileFgSecondaryDisplay() { - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, FG, SECONDARY_DISPLAY_ID); + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, FG, SECONDARY_DISPLAY_ID); - assertStartUserResult(result, START_USER_RESULT_FAILURE); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE); assertCurrentUser(INITIAL_CURRENT_USER_ID); + + stopUserAndAssertState(USER_ID); } @Test public final void testGetStartedProfileGroupId_whenStartedWithNoProfileGroupId() { - int result = mMediator.startUser(USER_ID, NO_PROFILE_GROUP_ID, FG, DEFAULT_DISPLAY); - assertStartUserResult(result, START_USER_RESULT_SUCCESS_VISIBLE); + int result = mMediator.startOnly(USER_ID, NO_PROFILE_GROUP_ID, FG, DEFAULT_DISPLAY); + assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); assertWithMessage("shit").that(mMediator.getStartedProfileGroupId(USER_ID)) .isEqualTo(USER_ID); @@ -386,59 +399,78 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { .isEqualTo(USER_ID); } + /** + * Stops the given user and assert the proper state is set. + * + * <p>This method should be called at the end of tests that starts a user, so it can test + * {@code stopUser()} as well (technically speaking, {@code stopUser()} should be tested on its + * own methods, but it depends on the user being started at first place, so pragmatically + * speaking, it's better to "reuse" such tests for both (start and stop) + */ + private void stopUserAndAssertState(@UserIdInt int userId) { + mMediator.stopUser(userId); + + assertUserIsStopped(userId); + assertNoUserAssignedToDisplay(); + } + // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining // it's not meant to be used to test startUser() itself. protected void mockCurrentUser(@UserIdInt int userId) { Log.d(TAG, "mockCurrentUser(" + userId + ")"); - int result = mMediator.startUser(userId, userId, FG, DEFAULT_DISPLAY); - if (result != START_USER_RESULT_SUCCESS_VISIBLE) { + int result = mMediator.startOnly(userId, userId, FG, DEFAULT_DISPLAY); + if (result != USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE) { throw new IllegalStateException("Failed to mock current user " + userId - + ": mediator returned " + startUserResultToString(result)); + + ": mediator returned " + userAssignmentResultToString(result)); } } - // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining + // TODO(b/244644281): remove when start & assign are merged; or add a note explaining // it's not meant to be used to test startUser() itself. protected void startDefaultProfile() { mockCurrentUser(PARENT_USER_ID); Log.d(TAG, "starting default profile (" + PROFILE_USER_ID + ") in background after starting" + " its parent (" + PARENT_USER_ID + ") on foreground"); - int result = mMediator.startUser(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); - if (result != START_USER_RESULT_SUCCESS_VISIBLE) { + int result = mMediator.startOnly(PROFILE_USER_ID, PARENT_USER_ID, BG, DEFAULT_DISPLAY); + if (result != USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE) { throw new IllegalStateException("Failed to start profile user " + PROFILE_USER_ID - + ": mediator returned " + startUserResultToString(result)); + + ": mediator returned " + userAssignmentResultToString(result)); } } - // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining + // TODO(b/244644281): remove when start & assign are merged; or add a note explaining // it's not meant to be used to test stopUser() itself. protected void stopDefaultProfile() { Log.d(TAG, "stopping default profile"); mMediator.stopUser(PROFILE_USER_ID); } - // TODO(b/244644281): remove if start & assign are merged; if they aren't, add a note explaining + // TODO(b/244644281): remove when start & assign are merged; or add a note explaining // it's not meant to be used to test assignUserToDisplay() itself. protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) { Log.d(TAG, "assignUserToDisplay(" + userId + ", " + displayId + ")"); - int result = mMediator.startUser(userId, userId, BG, displayId); - if (result != START_USER_RESULT_SUCCESS_INVISIBLE) { + int result = mMediator.startOnly(userId, userId, BG, displayId); + if (result != USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE) { throw new IllegalStateException("Failed to startuser " + userId - + " on background: mediator returned " + startUserResultToString(result)); + + " on background: mediator returned " + userAssignmentResultToString(result)); } mMediator.assignUserToDisplay(userId, userId, displayId); } + // TODO(b/244644281): remove when start & assign are merged; or rename to + // assertNoUserAssignedToSecondaryDisplays protected final void assertNoUserAssignedToDisplay() { - assertWithMessage("uses on secondary displays") + assertWithMessage("users on secondary displays") .that(mMediator.getUsersOnSecondaryDisplays()) .isEmpty(); } + // TODO(b/244644281): remove when start & assign are merged; or rename to + // assertUserAssignedToSecondaryDisplay protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) { - assertWithMessage("uses on secondary displays") + assertWithMessage("users on secondary displays") .that(mMediator.getUsersOnSecondaryDisplays()) .containsExactly(userId, displayId); } @@ -446,24 +478,44 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { private void assertCurrentUser(@UserIdInt int userId) { assertWithMessage("mediator.getCurrentUserId()").that(mMediator.getCurrentUserId()) .isEqualTo(userId); + if (userId != INITIAL_CURRENT_USER_ID) { + assertUserIsStarted(userId); + } + } + + private void assertUserIsStarted(@UserIdInt int userId) { + assertWithMessage("mediator.isStarted(%s)", userId).that(mMediator.isStartedUser(userId)) + .isTrue(); + } + + private void assertUserIsStopped(@UserIdInt int userId) { + assertWithMessage("mediator.isStarted(%s)", userId).that(mMediator.isStartedUser(userId)) + .isFalse(); } - private void assertIsStartedProfile(@UserIdInt int userId) { + private void assertProfileIsStarted(@UserIdInt int userId) { assertWithMessage("mediator.isStartedProfile(%s)", userId) .that(mMediator.isStartedProfile(userId)) .isTrue(); + assertUserIsStarted(userId); } - private void assertStartedProfileGroupIdOf(@UserIdInt int profileId, @UserIdInt int parentId) { - assertWithMessage("mediator.getStartedProfileGroupId(%s)", profileId) - .that(mMediator.getStartedProfileGroupId(profileId)) - .isEqualTo(parentId); + private void assertStartedProfileGroupIdOf(@UserIdInt int userId, + @UserIdInt int profileGroupId) { + assertWithMessage("mediator.getStartedProfileGroupId(%s)", userId) + .that(mMediator.getStartedProfileGroupId(userId)) + .isEqualTo(profileGroupId); } - private void assertIsCurrentUserOrRunningProfileOfCurrentUser(int userId) { + private void assertIsCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) { assertWithMessage("mediator.isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId) .that(mMediator.isCurrentUserOrRunningProfileOfCurrentUser(userId)) .isTrue(); + if (mMediator.getCurrentUserId() == userId) { + assertUserIsStarted(userId); + } else { + assertProfileIsStarted(userId); + } } private void assertIsNotCurrentUserOrRunningProfileOfCurrentUser(int userId) { @@ -474,8 +526,8 @@ abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase { private void assertStartUserResult(int actualResult, int expectedResult) { assertWithMessage("startUser() result (where %s=%s and %s=%s)", - actualResult, startUserResultToString(actualResult), - expectedResult, startUserResultToString(expectedResult)) + actualResult, userAssignmentResultToString(actualResult), + expectedResult, userAssignmentResultToString(expectedResult)) .that(actualResult).isEqualTo(expectedResult); } } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 80cee50cef79..a49214f9b4f5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -39,6 +39,8 @@ import static com.android.server.am.UserController.USER_CURRENT_MSG; import static com.android.server.am.UserController.USER_START_MSG; import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG; import static com.android.server.am.UserController.USER_VISIBILITY_CHANGED_MSG; +import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE; +import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE; import static com.google.android.collect.Lists.newArrayList; import static com.google.android.collect.Sets.newHashSet; @@ -100,6 +102,7 @@ import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.am.UserState.KeyEvictedCallback; import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserAssignmentResult; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; @@ -162,10 +165,15 @@ public class UserControllerTest { USER_VISIBILITY_CHANGED_MSG, USER_CURRENT_MSG); - private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet( + private static final Set<Integer> START_INVISIBLE_BACKGROUND_USER_MESSAGE_CODES = newHashSet( USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG); + private static final Set<Integer> START_VISIBLE_BACKGROUND_USER_MESSAGE_CODES = newHashSet( + USER_START_MSG, + USER_VISIBILITY_CHANGED_MSG, + REPORT_LOCKED_BOOT_COMPLETE_MSG); + @Before public void setUp() throws Exception { runWithDexmakerShareClassLoader(() -> { @@ -184,6 +192,12 @@ public class UserControllerTest { mockIsUsersOnSecondaryDisplaysEnabled(false); // All UserController params are set to default. + // Starts with a generic assumption that the user starts visible, but on tests where + // that's not the case, the test should call mockAssignUserToMainDisplay() + doReturn(UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE) + .when(mInjector.mUserManagerInternalMock) + .assignUserToDisplayOnStart(anyInt(), anyInt(), anyBoolean(), anyInt()); + mUserController = new UserController(mInjector); mUserController.setAllowUserUnlocking(true); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); @@ -211,16 +225,29 @@ public class UserControllerTest { @Test public void testStartUser_background() { + mockAssignUserToMainDisplay(TEST_USER_ID, /* foreground= */ false, + USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE); boolean started = mUserController.startUser(TEST_USER_ID, /* foreground= */ false); assertWithMessage("startUser(%s, foreground=false)", TEST_USER_ID).that(started).isTrue(); verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); verify(mInjector, never()).clearAllLockedTasks(anyString()); - startBackgroundUserAssertions(); + startBackgroundUserAssertions(/*visible= */ false); verifyUserAssignedToDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY); } @Test + public void testStartUser_displayAssignmentFailed() { + doReturn(UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE) + .when(mInjector.mUserManagerInternalMock) + .assignUserToDisplayOnStart(eq(TEST_USER_ID), anyInt(), eq(true), anyInt()); + + boolean started = mUserController.startUser(TEST_USER_ID, /* foreground= */ true); + + assertWithMessage("startUser(%s, foreground=true)", TEST_USER_ID).that(started).isFalse(); + } + + @Test public void testStartUserOnSecondaryDisplay_defaultDisplay() { assertThrows(IllegalArgumentException.class, () -> mUserController .startUserOnSecondaryDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY)); @@ -240,7 +267,7 @@ public class UserControllerTest { verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); verify(mInjector, never()).clearAllLockedTasks(anyString()); - startBackgroundUserAssertions(); + startBackgroundUserAssertions(/*visible= */ true); } @Test @@ -266,6 +293,8 @@ public class UserControllerTest { @Test public void testStartPreCreatedUser_background() throws Exception { + mockAssignUserToMainDisplay(TEST_PRE_CREATED_USER_ID, /* foreground= */ false, + USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE); assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false)); // Make sure no intents have been fired for pre-created users. assertTrue(mInjector.mSentIntents.isEmpty()); @@ -284,8 +313,6 @@ public class UserControllerTest { // binder calls, but their side effects (in this case, that the user is stopped right away) assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes()) .containsExactly(USER_START_MSG); - - verifyUserNeverAssignedToDisplay(); } private void startUserAssertions( @@ -295,8 +322,10 @@ public class UserControllerTest { assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes); } - private void startBackgroundUserAssertions() { - startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES); + private void startBackgroundUserAssertions(boolean visible) { + startUserAssertions(START_BACKGROUND_USER_ACTIONS, + visible ? START_VISIBLE_BACKGROUND_USER_MESSAGE_CODES + : START_INVISIBLE_BACKGROUND_USER_MESSAGE_CODES); } private void startForegroundUserAssertions() { @@ -680,19 +709,24 @@ public class UserControllerTest { @Test public void testStartProfile() throws Exception { + mockAssignUserToMainDisplay(TEST_PRE_CREATED_USER_ID, /* foreground= */ false, + USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE); setUpAndStartProfileInBackground(TEST_USER_ID1); - startBackgroundUserAssertions(); + startBackgroundUserAssertions(/*visible= */ true); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @Test public void testStartProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception { + mockAssignUserToMainDisplay(TEST_USER_ID1, /* foreground= */ false, + USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE); + mockIsUsersOnSecondaryDisplaysEnabled(true); setUpAndStartProfileInBackground(TEST_USER_ID1); - startBackgroundUserAssertions(); + startBackgroundUserAssertions(/*visible= */ true); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @@ -949,22 +983,29 @@ public class UserControllerTest { when(mInjector.isUsersOnSecondaryDisplaysEnabled()).thenReturn(value); } + private void mockAssignUserToMainDisplay(@UserIdInt int userId, boolean foreground, + @UserAssignmentResult int result) { + when(mInjector.mUserManagerInternalMock.assignUserToDisplayOnStart(eq(userId), + /* profileGroupId= */ anyInt(), eq(foreground), eq(Display.DEFAULT_DISPLAY))) + .thenReturn(result); + } + private void verifyUserAssignedToDisplay(@UserIdInt int userId, int displayId) { - verify(mInjector.getUserManagerInternal()).assignUserToDisplay(eq(userId), anyInt(), + verify(mInjector.getUserManagerInternal()).assignUserToDisplayOnStart(eq(userId), anyInt(), anyBoolean(), eq(displayId)); } private void verifyUserNeverAssignedToDisplay() { - verify(mInjector.getUserManagerInternal(), never()).assignUserToDisplay(anyInt(), anyInt(), - anyBoolean(), anyInt()); + verify(mInjector.getUserManagerInternal(), never()).assignUserToDisplayOnStart(anyInt(), + anyInt(), anyBoolean(), anyInt()); } private void verifyUserUnassignedFromDisplay(@UserIdInt int userId) { - verify(mInjector.getUserManagerInternal()).unassignUserFromDisplay(userId); + verify(mInjector.getUserManagerInternal()).unassignUserFromDisplayOnStop(userId); } private void verifyUserUnassignedFromDisplayNeverCalled(@UserIdInt int userId) { - verify(mInjector.getUserManagerInternal(), never()).unassignUserFromDisplay(userId); + verify(mInjector.getUserManagerInternal(), never()).unassignUserFromDisplayOnStop(userId); } private void verifySystemUserVisibilityChangedNotified(boolean visible) { |