diff options
4 files changed, 618 insertions, 160 deletions
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 2a8c88ec05cd..ef8e94e3c739 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -630,7 +630,7 @@ public class UserManagerService extends IUserManager.Stub { /** * Set on on devices that support background users (key) running on secondary displays (value). */ - // TODO(b/239982558): move such logic to a different class (like UserDisplayAssigner) + // TODO(b/244644281): move such logic to a different class (like UserDisplayAssigner) @Nullable @GuardedBy("mUsersOnSecondaryDisplays") private final SparseIntArray mUsersOnSecondaryDisplays; @@ -710,7 +710,8 @@ public class UserManagerService extends IUserManager.Stub { @VisibleForTesting UserManagerService(Context context) { this(context, /* pm= */ null, /* userDataPreparer= */ null, - /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null); + /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null, + /* usersOnSecondaryDisplays= */ null); } /** @@ -721,13 +722,13 @@ public class UserManagerService extends IUserManager.Stub { UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer, Object packagesLock) { this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(), - /* users= */ null); + /* users= */ null, /* usersOnSecondaryDisplays= */ null); } @VisibleForTesting UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer, Object packagesLock, File dataDir, - SparseArray<UserData> users) { + SparseArray<UserData> users, @Nullable SparseIntArray usersOnSecondaryDisplays) { mContext = context; mPm = pm; mPackagesLock = packagesLock; @@ -758,7 +759,13 @@ public class UserManagerService extends IUserManager.Stub { mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; emulateSystemUserModeIfNeeded(); mUsersOnSecondaryDisplaysEnabled = UserManager.isUsersOnSecondaryDisplaysEnabled(); - mUsersOnSecondaryDisplays = mUsersOnSecondaryDisplaysEnabled ? new SparseIntArray() : null; + if (mUsersOnSecondaryDisplaysEnabled) { + mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null + ? new SparseIntArray() // default behavior + : usersOnSecondaryDisplays; // passed by unit test + } else { + mUsersOnSecondaryDisplays = null; + } } void systemReady() { @@ -1720,6 +1727,11 @@ public class UserManagerService extends IUserManager.Stub { return userId == getCurrentUserId(); } + @VisibleForTesting + boolean isUsersOnSecondaryDisplaysEnabled() { + return mUsersOnSecondaryDisplaysEnabled; + } + @Override public boolean isUserVisible(@UserIdInt int userId) { int callingUserId = UserHandle.getCallingUserId(); @@ -1733,7 +1745,8 @@ public class UserManagerService extends IUserManager.Stub { return isUserVisibleUnchecked(userId); } - private boolean isUserVisibleUnchecked(@UserIdInt int userId) { + @VisibleForTesting + boolean isUserVisibleUnchecked(@UserIdInt int userId) { // First check current foreground user and their profiles (on main display) if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) { return true; @@ -1750,8 +1763,8 @@ public class UserManagerService extends IUserManager.Stub { } } - // TODO(b/239982558): add unit test - private int getDisplayAssignedToUser(@UserIdInt int userId) { + @VisibleForTesting + int getDisplayAssignedToUser(@UserIdInt int userId) { if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) { return Display.DEFAULT_DISPLAY; } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java new file mode 100644 index 000000000000..7d7b70ca19f6 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.pm; + +/** + * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerInternalTest} + */ +public final class UserManagerInternalTest extends UserManagerServiceOrInternalTestCase { + + @Override + protected boolean isUserVisible(int userId) { + return mUmi.isUserVisible(userId); + } + + @Override + protected boolean isUserVisibleOnDisplay(int userId, int displayId) { + return mUmi.isUserVisible(userId, displayId); + } + + @Override + protected int getDisplayAssignedToUser(int userId) { + return mUmi.getDisplayAssignedToUser(userId); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java new file mode 100644 index 000000000000..e021e767e7c8 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.pm; + +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.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; +import android.content.Context; +import android.content.pm.UserInfo; +import android.os.UserManager; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import androidx.test.annotation.UiThreadTest; + +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; +import com.android.server.ExtendedMockitoTestCase; +import com.android.server.LocalServices; +import com.android.server.am.UserState; +import com.android.server.pm.UserManagerService.UserData; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +/** + * Base class for {@link UserManagerInternalTest} and {@link UserManagerInternalTest}. + * + * <p>{@link UserManagerService} and its {@link UserManagerInternal} implementation have a + * "symbiotic relationship - some methods of the former simply call the latter and vice versa. + * + * <p>Ideally, only one of them should have the logic, but since that's not the case, this class + * provices the infra to make it easier to test both (which in turn would make it easier / safer to + * refactor their logic later). + */ +abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestCase { + + private static final String TAG = UserManagerServiceOrInternalTestCase.class.getSimpleName(); + + /** + * Id for a simple user (that doesn't have profiles). + */ + protected static final int USER_ID = 600; + + /** + * Id for another simple user. + */ + protected static final int OTHER_USER_ID = 666; + + /** + * Id for a user that has one profile (whose id is {@link #PROFILE_USER_ID}. + * + * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service. + */ + protected static final int PARENT_USER_ID = 642; + + /** + * Id for a profile whose parent is {@link #PARENTUSER_ID}. + * + * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service. + */ + protected static final int PROFILE_USER_ID = 643; + + /** + * Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}). + */ + private static final int SECONDARY_DISPLAY_ID = 42; + + private final Object mPackagesLock = new Object(); + private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() + .getTargetContext(); + private final SparseArray<UserData> mUsers = new SparseArray<>(); + + // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation + // details into the unit test, but it's fine for now - in the long term, this logic should be + // refactored into a proper UserDisplayAssignment class. + private final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray(); + + private Context mSpiedContext; + private UserManagerService mStandardUms; + private UserManagerService mMumdUms; + private UserManagerInternal mStandardUmi; + private UserManagerInternal mMumdUmi; + + private @Mock PackageManagerService mMockPms; + private @Mock UserDataPreparer mMockUserDataPreparer; + private @Mock ActivityManagerInternal mActivityManagerInternal; + + /** + * Reference to the {@link UserManagerService} being tested. + * + * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple + * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}. + */ + protected UserManagerService mUms; + + /** + * Reference to the {@link UserManagerInternal} being tested. + * + * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple + * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}. + */ + protected UserManagerInternal mUmi; + + @Override + protected void initializeSession(StaticMockitoSessionBuilder builder) { + builder + .spyStatic(UserManager.class) + .spyStatic(LocalServices.class); + } + + @Before + @UiThreadTest // Needed to initialize main handler + public final void setFixtures() { + mSpiedContext = spy(mRealContext); + + // Called when WatchedUserStates is constructed + doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache()); + + // Need to set both UserManagerService instances here, as they need to be run in the + // UiThread + + // mMumdUms / mMumdUmi + mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ true); + mMumdUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, + mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays); + assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()") + .that(mMumdUms.isUsersOnSecondaryDisplaysEnabled()) + .isTrue(); + mMumdUmi = LocalServices.getService(UserManagerInternal.class); + assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mMumdUmi) + .isNotNull(); + resetUserManagerInternal(); + + // mStandardUms / mStandardUmi + mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ false); + mStandardUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, + mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays); + assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()") + .that(mStandardUms.isUsersOnSecondaryDisplaysEnabled()) + .isFalse(); + mStandardUmi = LocalServices.getService(UserManagerInternal.class); + assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mStandardUmi) + .isNotNull(); + setServiceFixtures(/*usersOnSecondaryDisplaysEnabled= */ false); + } + + @After + public final void resetUserManagerInternal() { + // LocalServices follows the "Highlander rule" - There can be only one! + LocalServices.removeServiceForTest(UserManagerInternal.class); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Methods whose UMS implementation calls UMI or vice-versa - they're tested in this class, // + // but the subclass must provide the proper implementation // + ////////////////////////////////////////////////////////////////////////////////////////////// + + protected abstract boolean isUserVisible(int userId); + protected abstract boolean isUserVisibleOnDisplay(int userId, int displayId); + protected abstract int getDisplayAssignedToUser(int userId); + + ///////////////////////////////// + // Tests for the above methods // + ///////////////////////////////// + + @Test + public void testIsUserVisible_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_NULL).that(isUserVisible(USER_NULL)).isFalse(); + } + + @Test + public void testIsUserVisible_currentUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue(); + } + + @Test + public void testIsUserVisible_nonCurrentUser() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isFalse(); + } + + @Test + public void testIsUserVisible_startedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID)) + .isTrue(); + } + + @Test + public void testIsUserVisible_stoppedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID)) + .isFalse(); + } + + @Test + public void testIsUserVisible_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue(); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // isUserVisible() for bg users relies only on the user / display assignments + + @Test + public void testIsUserVisibleOnDisplay_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_NULL, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_NULL, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserInvalidDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, INVALID_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserDefaultDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserSecondaryDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_nonCurrentUserDefaultDisplay() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserSecondaryDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // isUserVisibleOnDisplay() for bg users relies only on the user / display assignments + + @Test + public void testGetDisplayAssignedToUser_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_NULL) + .that(getDisplayAssignedToUser(USER_NULL)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_currentUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_nonCurrentUser() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID) + .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID) + .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(SECONDARY_DISPLAY_ID); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // getDisplayAssignedToUser() for bg users relies only on the user / display assignments + + /////////////////////////////////////////// + // Helper methods exposed to sub-classes // + /////////////////////////////////////////// + + /** + * Change test fixtures to use a version that supports {@code MUMD} (Multiple Users on Multiple + * Displays). + */ + protected void enableUsersOnSecondaryDisplays() { + setServiceFixtures(/* usersOnSecondaryDisplaysEnabled= */ true); + } + + protected void mockCurrentUser(@UserIdInt int userId) { + mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); + + when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); + } + + protected <T> void mockGetLocalService(Class<T> serviceClass, T service) { + doReturn(service).when(() -> LocalServices.getService(serviceClass)); + } + + protected void addDefaultProfileAndParent() { + addUser(PARENT_USER_ID); + addProfile(PROFILE_USER_ID, PARENT_USER_ID); + } + + protected void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) { + TestUserData profileData = new TestUserData(profileId); + profileData.info.flags = UserInfo.FLAG_PROFILE; + profileData.info.profileGroupId = parentId; + + addUserData(profileData); + } + + protected void addUser(@UserIdInt int userId) { + TestUserData userData = new TestUserData(userId); + + addUserData(userData); + } + + protected void startDefaultProfile() { + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + } + + protected void stopDefaultProfile() { + // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead + removeUserState(PROFILE_USER_ID); + } + + // NOTE: should only called by tests that indirectly needs to check user assignments (like + // isUserVisible), not by tests for the user assignment methods per se. + protected void assignUserToDisplay(@UserIdInt int userId, int displayId) { + mUsersOnSecondaryDisplays.put(userId, displayId); + } + + /////////////////// + // Private infra // + /////////////////// + + private void setServiceFixtures(boolean usersOnSecondaryDisplaysEnabled) { + Log.d(TAG, "Setting fixtures for usersOnSecondaryDisplaysEnabled=" + + usersOnSecondaryDisplaysEnabled); + if (usersOnSecondaryDisplaysEnabled) { + mUms = mMumdUms; + mUmi = mMumdUmi; + } else { + mUms = mStandardUms; + mUmi = mStandardUmi; + } + } + + private void mockIsUsersOnSecondaryDisplaysEnabled(boolean enabled) { + Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled); + doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled()); + } + + private void addUserData(TestUserData userData) { + Log.d(TAG, "Adding " + userData); + mUsers.put(userData.info.id, userData); + } + + private void setUserState(@UserIdInt int userId, int userState) { + mUmi.setUserState(userId, userState); + } + + private void removeUserState(@UserIdInt int userId) { + mUmi.removeUserState(userId); + } + + private static final class TestUserData extends UserData { + + @SuppressWarnings("deprecation") + TestUserData(@UserIdInt int userId) { + info = new UserInfo(); + info.id = userId; + } + + @Override + public String toString() { + return "TestUserData[" + info.toFullString() + "]"; + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index 64931116bd01..b335a34f6f14 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -15,87 +15,20 @@ */ package com.android.server.pm; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; - import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; -import android.content.Context; -import android.content.pm.UserInfo; import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import android.util.SparseArray; - -import androidx.test.annotation.UiThreadTest; -import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; -import com.android.server.ExtendedMockitoTestCase; -import com.android.server.LocalServices; -import com.android.server.am.UserState; -import com.android.server.pm.UserManagerService.UserData; - -import org.junit.After; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; /** * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest} */ -public final class UserManagerServiceTest extends ExtendedMockitoTestCase { - - private static final String TAG = UserManagerServiceTest.class.getSimpleName(); - - private final Object mPackagesLock = new Object(); - private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() - .getTargetContext(); - private Context mSpiedContext; - - private @Mock PackageManagerService mMockPms; - private @Mock UserDataPreparer mMockUserDataPreparer; - private @Mock ActivityManagerInternal mActivityManagerInternal; - - private final SparseArray<UserData> mUsers = new SparseArray<>(); - private UserManagerService mUms; - private UserManagerInternal mUmi; - - @Override - protected void initializeSession(StaticMockitoSessionBuilder builder) { - builder - .spyStatic(UserManager.class) - .spyStatic(LocalServices.class); - } - - @Before - @UiThreadTest // Needed to initialize main handler - public void setFixtures() { - mSpiedContext = spy(mRealContext); - - // Called when WatchedUserStates is constructed - doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache()); - - mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, - mPackagesLock, mRealContext.getDataDir(), mUsers); - mUmi = LocalServices.getService(UserManagerInternal.class); - - assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi) - .isNotNull(); - } - - @After - public void resetLocalService() { - // LocalServices follows the "Highlander rule" - There can be only one! - LocalServices.removeServiceForTest(UserManagerInternal.class); - } +public final class UserManagerServiceTest extends UserManagerServiceOrInternalTestCase { @Test - public void testgetCurrentUserId_amInternalNotReady() { + public void testGetCurrentUserId_amInternalNotReady() { mockGetLocalService(ActivityManagerInternal.class, null); assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId()) @@ -103,119 +36,70 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { } @Test - public void testgetCurrentUserId() { - mockCurrentUser(42); + public void testGetCurrentUserId() { + mockCurrentUser(USER_ID); assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId()) - .isEqualTo(42); + .isEqualTo(USER_ID); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_currentUser() { - int userId = 42; - mockCurrentUser(userId); + mockCurrentUser(USER_ID); - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isTrue(); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isTrue(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_notCurrentUser() { - int userId = 42; - mockCurrentUser(108); + mockCurrentUser(OTHER_USER_ID); - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isFalse(); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isFalse(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_startedProfileOfCurrentUser() { - int parentId = 108; - int profileId = 42; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(parentId); - setUserState(profileId, UserState.STATE_RUNNING_UNLOCKED); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isTrue(); + addDefaultProfileAndParent(); + startDefaultProfile(); + mockCurrentUser(PARENT_USER_ID); + + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isTrue(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_stoppedProfileOfCurrentUser() { - int parentId = 108; - int profileId = 42; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(parentId); - // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead - removeUserState(profileId); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse(); + addDefaultProfileAndParent(); + stopDefaultProfile(); + mockCurrentUser(PARENT_USER_ID); + + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_profileOfNonCurrentUSer() { - int parentId = 108; - int profileId = 42; - int currentUserId = 666; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(currentUserId); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse(); - } - - private void mockCurrentUser(@UserIdInt int userId) { - mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); - - when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); - } - - private <T> void mockGetLocalService(Class<T> serviceClass, T service) { - doReturn(service).when(() -> LocalServices.getService(serviceClass)); - } - - private void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) { - TestUserData profileData = new TestUserData(profileId); - profileData.info.flags = UserInfo.FLAG_PROFILE; - profileData.info.profileGroupId = parentId; - - addUserData(profileData); - } - - private void addUser(@UserIdInt int userId) { - TestUserData userData = new TestUserData(userId); + addDefaultProfileAndParent(); + mockCurrentUser(OTHER_USER_ID); - addUserData(userData); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse(); } - private void addUserData(TestUserData userData) { - Log.d(TAG, "Adding " + userData); - mUsers.put(userData.info.id, userData); - } - - private void setUserState(@UserIdInt int userId, int userState) { - mUmi.setUserState(userId, userState); + @Override + protected boolean isUserVisible(int userId) { + return mUms.isUserVisibleUnchecked(userId); } - private void removeUserState(@UserIdInt int userId) { - mUmi.removeUserState(userId); + @Override + protected boolean isUserVisibleOnDisplay(int userId, int displayId) { + return mUms.isUserVisibleOnDisplay(userId, displayId); } - private static final class TestUserData extends UserData { - - @SuppressWarnings("unused") - TestUserData(@UserIdInt int userId) { - info = new UserInfo(); - info.id = userId; - } - - @Override - public String toString() { - return "TestUserData[" + info.toFullString() + "]"; - } + @Override + protected int getDisplayAssignedToUser(int userId) { + return mUms.getDisplayAssignedToUser(userId); } } |