From 02debd9ba1e153ab9f439abe412a5d977cc612c3 Mon Sep 17 00:00:00 2001 From: Alex Stetson Date: Wed, 14 Jun 2023 13:59:43 -0700 Subject: [PATCH] Ensure secondary sysui service starts If a system boots and switches into a secondary user prior to SystemUI initializing, the ACTION_USER_SWITCHED intent will not be received and the secondary service will never be started. By checking the current user on init, it is ensured that we do not miss this case. Bug: 286338771 Test: manual, atest com.android.systemui.user Change-Id: Ic3110213db96c29f7943604c451aec1ac9b254fc --- .../user/domain/interactor/UserInteractor.kt | 4 +- .../data/repository/ShadeInteractorTest.kt | 19 ++ .../domain/interactor/UserInteractorTest.kt | 286 ++++++++++++------ .../StatusBarUserChipViewModelTest.kt | 5 + .../ui/viewmodel/UserSwitcherViewModelTest.kt | 14 + 5 files changed, 242 insertions(+), 86 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index a487f538e59ce..ab4ead6749cba 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -27,6 +27,7 @@ import android.content.pm.UserInfo import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.Icon +import android.os.Process import android.os.RemoteException import android.os.UserHandle import android.os.UserManager @@ -334,6 +335,7 @@ constructor( onBroadcastReceived(intent, previousSelectedUser) } .launchIn(applicationScope) + restartSecondaryService(repository.getSelectedUserInfo().id) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) } @@ -646,7 +648,7 @@ constructor( // Connect to the new secondary user's service (purely to ensure that a persistent // SystemUI application is created for that user) - if (userId != UserHandle.USER_SYSTEM) { + if (userId != Process.myUserHandle().identifier) { applicationContext.startServiceAsUser( intent, UserHandle.of(userId), diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index 3ea8f5412b403..cdcd1a257671b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -20,6 +20,7 @@ import android.app.ActivityManager import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.content.pm.UserInfo import android.os.UserManager import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger @@ -47,6 +48,7 @@ import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -92,6 +94,23 @@ class ShadeInteractorTest : SysuiTestCase() { mainDispatcher = testDispatcher, repository = userRepository, ) + + runBlocking { + val userInfos = + listOf( + UserInfo( + /* id= */ 0, + /* name= */ "zero", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_PRIMARY or + UserInfo.FLAG_ADMIN or + UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + ) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + } userInteractor = UserInteractor( applicationContext = context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index 89dce6151de97..5b418eddc4f5c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -19,10 +19,12 @@ package com.android.systemui.user.domain.interactor import android.app.ActivityManager import android.app.admin.DevicePolicyManager +import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.os.Process import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -62,7 +64,9 @@ import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertNotNull import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -76,6 +80,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.never +import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -99,11 +104,15 @@ class UserInteractorTest : SysuiTestCase() { private lateinit var underTest: UserInteractor + private lateinit var spyContext: Context private lateinit var testScope: TestScope private lateinit var userRepository: FakeUserRepository + private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var telephonyRepository: FakeTelephonyRepository + private lateinit var testDispatcher: TestDispatcher private lateinit var featureFlags: FakeFeatureFlags + private lateinit var refreshUsersScheduler: RefreshUsersScheduler @Before fun setUp() { @@ -123,58 +132,36 @@ class UserInteractorTest : SysuiTestCase() { set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.FACE_AUTH_REFACTOR, true) } - val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags) - keyguardRepository = reply.repository + spyContext = spy(context) + keyguardReply = KeyguardInteractorFactory.create(featureFlags = featureFlags) + keyguardRepository = keyguardReply.repository userRepository = FakeUserRepository() telephonyRepository = FakeTelephonyRepository() - val testDispatcher = StandardTestDispatcher() + testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) - val refreshUsersScheduler = + refreshUsersScheduler = RefreshUsersScheduler( applicationScope = testScope.backgroundScope, mainDispatcher = testDispatcher, repository = userRepository, ) - underTest = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = reply.keyguardInteractor, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = telephonyRepository, - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = - GuestUserInteractor( - applicationContext = context, - applicationScope = testScope.backgroundScope, - mainDispatcher = testDispatcher, - backgroundDispatcher = testDispatcher, - manager = manager, - repository = userRepository, - deviceProvisionedController = deviceProvisionedController, - devicePolicyManager = devicePolicyManager, - refreshUsersScheduler = refreshUsersScheduler, - uiEventLogger = uiEventLogger, - resumeSessionReceiver = resumeSessionReceiver, - resetOrExitSessionReceiver = resetOrExitSessionReceiver, - ), - uiEventLogger = uiEventLogger, - featureFlags = featureFlags, - ) } @Test - fun testKeyguardUpdateMonitor_onKeyguardGoingAway() = + fun createUserInteractor_processUser_noSecondaryService() { + createUserInteractor() + verify(spyContext, never()).startServiceAsUser(any(), any()) + } + + @Test + fun createUserInteractor_nonProcessUser_startsSecondaryService() { + createUserInteractor(false /* startAsProcessUser */) + verify(spyContext).startServiceAsUser(any(), any()) + } + + @Test + fun testKeyguardUpdateMonitor_onKeyguardGoingAway() { + createUserInteractor() testScope.runTest { val argumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) verify(keyguardUpdateMonitor).registerCallback(argumentCaptor.capture()) @@ -184,9 +171,11 @@ class UserInteractorTest : SysuiTestCase() { val lastValue = collectLastValue(underTest.dialogDismissRequests) assertNotNull(lastValue) } + } @Test - fun onRecordSelected_user() = + fun onRecordSelected_user() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -201,9 +190,11 @@ class UserInteractorTest : SysuiTestCase() { verify(activityManager).switchUser(userInfos[1].id) Unit } + } @Test - fun onRecordSelected_switchToGuestUser() = + fun onRecordSelected_switchToGuestUser() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -217,9 +208,11 @@ class UserInteractorTest : SysuiTestCase() { verify(activityManager).switchUser(userInfos.last().id) Unit } + } @Test - fun onRecordSelected_switchToRestrictedUser() = + fun onRecordSelected_switchToRestrictedUser() { + createUserInteractor() testScope.runTest { var userInfos = createUserInfos(count = 2, includeGuest = false).toMutableList() userInfos.add( @@ -242,9 +235,11 @@ class UserInteractorTest : SysuiTestCase() { verify(activityManager).switchUser(userInfos.last().id) Unit } + } @Test - fun onRecordSelected_enterGuestMode() = + fun onRecordSelected_enterGuestMode() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -262,9 +257,11 @@ class UserInteractorTest : SysuiTestCase() { verify(manager).createGuest(any()) Unit } + } @Test - fun onRecordSelected_action() = + fun onRecordSelected_action() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -278,9 +275,11 @@ class UserInteractorTest : SysuiTestCase() { verify(dialogShower, never()).dismiss() verify(activityStarter).startActivity(any(), anyBoolean()) } + } @Test - fun users_switcherEnabled() = + fun users_switcherEnabled() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -291,9 +290,11 @@ class UserInteractorTest : SysuiTestCase() { assertUsers(models = value(), count = 3, includeGuest = true) } + } @Test - fun users_switchesToSecondUser() = + fun users_switchesToSecondUser() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -305,9 +306,11 @@ class UserInteractorTest : SysuiTestCase() { assertUsers(models = value(), count = 2, selectedIndex = 1) } + } @Test - fun users_switcherNotEnabled() = + fun users_switcherNotEnabled() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -317,9 +320,11 @@ class UserInteractorTest : SysuiTestCase() { val value = collectLastValue(underTest.users) assertUsers(models = value(), count = 1) } + } @Test - fun selectedUser() = + fun selectedUser() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -332,9 +337,11 @@ class UserInteractorTest : SysuiTestCase() { userRepository.setSelectedUserInfo(userInfos[1]) assertUser(value(), id = 1, isSelected = true) } + } @Test - fun actions_deviceUnlocked() = + fun actions_deviceUnlocked() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) @@ -356,9 +363,11 @@ class UserInteractorTest : SysuiTestCase() { ) ) } + } @Test - fun actions_deviceUnlocked_fullScreen() = + fun actions_deviceUnlocked_fullScreen() { + createUserInteractor() testScope.runTest { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) val userInfos = createUserInfos(count = 2, includeGuest = false) @@ -379,9 +388,11 @@ class UserInteractorTest : SysuiTestCase() { ) ) } + } @Test - fun actions_deviceUnlockedUserNotPrimary_emptyList() = + fun actions_deviceUnlockedUserNotPrimary_emptyList() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -392,9 +403,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(value()).isEqualTo(emptyList()) } + } @Test - fun actions_deviceUnlockedUserIsGuest_emptyList() = + fun actions_deviceUnlockedUserIsGuest_emptyList() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = true) assertThat(userInfos[1].isGuest).isTrue() @@ -406,9 +419,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(value()).isEqualTo(emptyList()) } + } @Test - fun actions_deviceLockedAddFromLockscreenSet_fullList() = + fun actions_deviceLockedAddFromLockscreenSet_fullList() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -432,9 +447,11 @@ class UserInteractorTest : SysuiTestCase() { ) ) } + } @Test - fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() = + fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() { + createUserInteractor() testScope.runTest { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) val userInfos = createUserInfos(count = 2, includeGuest = false) @@ -459,9 +476,11 @@ class UserInteractorTest : SysuiTestCase() { ) ) } + } @Test - fun actions_deviceLocked_onlymanageUserIsShown() = + fun actions_deviceLocked_onlymanageUserIsShown() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -472,9 +491,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(value()).isEqualTo(listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)) } + } @Test - fun executeAction_addUser_dismissesDialogAndStartsActivity() = + fun executeAction_addUser_dismissesDialogAndStartsActivity() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -486,9 +507,11 @@ class UserInteractorTest : SysuiTestCase() { .log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER) underTest.onDialogShown() } + } @Test - fun executeAction_addSupervisedUser_dismissesDialogAndStartsActivity() = + fun executeAction_addSupervisedUser_dismissesDialogAndStartsActivity() { + createUserInteractor() testScope.runTest { underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER) @@ -500,9 +523,11 @@ class UserInteractorTest : SysuiTestCase() { .isEqualTo(UserManager.ACTION_CREATE_SUPERVISED_USER) assertThat(intentCaptor.value.`package`).isEqualTo(SUPERVISED_USER_CREATION_APP_PACKAGE) } + } @Test - fun executeAction_navigateToManageUsers() = + fun executeAction_navigateToManageUsers() { + createUserInteractor() testScope.runTest { underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) @@ -510,9 +535,11 @@ class UserInteractorTest : SysuiTestCase() { verify(activityStarter).startActivity(intentCaptor.capture(), eq(true)) assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS) } + } @Test - fun executeAction_guestMode() = + fun executeAction_guestMode() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -548,9 +575,11 @@ class UserInteractorTest : SysuiTestCase() { ) verify(activityManager).switchUser(guestUserInfo.id) } + } @Test - fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() = + fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = true) val guestUserInfo = userInfos[1] @@ -569,9 +598,11 @@ class UserInteractorTest : SysuiTestCase() { .isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java) verify(dialogShower, never()).dismiss() } + } @Test - fun selectUser_currentlyGuestNonGuestSelected_exitGuestDialog() = + fun selectUser_currentlyGuestNonGuestSelected_exitGuestDialog() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = true) val guestUserInfo = userInfos[1] @@ -587,9 +618,11 @@ class UserInteractorTest : SysuiTestCase() { .isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java) verify(dialogShower, never()).dismiss() } + } @Test - fun selectUser_notCurrentlyGuest_switchesUsers() = + fun selectUser_notCurrentlyGuest_switchesUsers() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -603,9 +636,11 @@ class UserInteractorTest : SysuiTestCase() { verify(activityManager).switchUser(userInfos[1].id) verify(dialogShower).dismiss() } + } @Test - fun telephonyCallStateChanges_refreshesUsers() = + fun telephonyCallStateChanges_refreshesUsers() { + createUserInteractor() testScope.runTest { runCurrent() @@ -616,9 +651,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) } + } @Test - fun userSwitchedBroadcast() = + fun userSwitchedBroadcast() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -634,7 +671,7 @@ class UserInteractorTest : SysuiTestCase() { userRepository.setSelectedUserInfo(userInfos[1]) runCurrent() fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( - context, + spyContext, Intent(Intent.ACTION_USER_SWITCHED) .putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id), ) @@ -644,10 +681,13 @@ class UserInteractorTest : SysuiTestCase() { verify(callback2, atLeastOnce()).onUserStateChanged() assertThat(userRepository.secondaryUserId).isEqualTo(userInfos[1].id) assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) + verify(spyContext).startServiceAsUser(any(), eq(UserHandle.of(userInfos[1].id))) } + } @Test - fun userInfoChangedBroadcast() = + fun userInfoChangedBroadcast() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -655,7 +695,7 @@ class UserInteractorTest : SysuiTestCase() { val refreshUsersCallCount = userRepository.refreshUsersCallCount fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( - context, + spyContext, Intent(Intent.ACTION_USER_INFO_CHANGED), ) @@ -663,9 +703,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) } + } @Test - fun systemUserUnlockedBroadcast_refreshUsers() = + fun systemUserUnlockedBroadcast_refreshUsers() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -673,7 +715,7 @@ class UserInteractorTest : SysuiTestCase() { val refreshUsersCallCount = userRepository.refreshUsersCallCount fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( - context, + spyContext, Intent(Intent.ACTION_USER_UNLOCKED) .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM), ) @@ -681,9 +723,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) } + } @Test - fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() = + fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -691,15 +735,17 @@ class UserInteractorTest : SysuiTestCase() { val refreshUsersCallCount = userRepository.refreshUsersCallCount fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( - context, + spyContext, Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337), ) assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount) } + } @Test - fun userRecords() = + fun userRecords() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = false) userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) @@ -723,9 +769,11 @@ class UserInteractorTest : SysuiTestCase() { ), ) } + } @Test - fun userRecordsFullScreen() = + fun userRecordsFullScreen() { + createUserInteractor() testScope.runTest { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) val userInfos = createUserInfos(count = 3, includeGuest = false) @@ -750,9 +798,11 @@ class UserInteractorTest : SysuiTestCase() { ), ) } + } @Test - fun selectedUserRecord() = + fun selectedUserRecord() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) @@ -768,9 +818,11 @@ class UserInteractorTest : SysuiTestCase() { isSwitchToEnabled = true, ) } + } @Test - fun users_secondaryUser_guestUserCanBeSwitchedTo() = + fun users_secondaryUser_guestUserCanBeSwitchedTo() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -781,9 +833,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(res()?.size == 3).isTrue() assertThat(res()?.find { it.isGuest }).isNotNull() } + } @Test - fun users_secondaryUser_noGuestAction() = + fun users_secondaryUser_noGuestAction() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -793,9 +847,11 @@ class UserInteractorTest : SysuiTestCase() { val res = collectLastValue(underTest.actions) assertThat(res()?.find { it == UserActionModel.ENTER_GUEST_MODE }).isNull() } + } @Test - fun users_secondaryUser_noGuestUserRecord() = + fun users_secondaryUser_noGuestUserRecord() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = true) userRepository.setUserInfos(userInfos) @@ -804,9 +860,11 @@ class UserInteractorTest : SysuiTestCase() { assertThat(underTest.userRecords.value.find { it.isGuest }).isNull() } + } @Test - fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() = + fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() { + createUserInteractor() testScope.runTest { val expandable = mock() underTest.showUserSwitcher(expandable) @@ -820,9 +878,11 @@ class UserInteractorTest : SysuiTestCase() { underTest.onDialogShown() assertThat(dialogRequest()).isNull() } + } @Test - fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() = + fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() { + createUserInteractor() testScope.runTest { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) @@ -838,9 +898,11 @@ class UserInteractorTest : SysuiTestCase() { underTest.onDialogShown() assertThat(dialogRequest()).isNull() } + } @Test - fun users_secondaryUser_managedProfileIsNotIncluded() = + fun users_secondaryUser_managedProfileIsNotIncluded() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList() userInfos.add( @@ -858,9 +920,11 @@ class UserInteractorTest : SysuiTestCase() { val res = collectLastValue(underTest.users) assertThat(res()?.size == 3).isTrue() } + } @Test - fun currentUserIsNotPrimaryAndUserSwitcherIsDisabled() = + fun currentUserIsNotPrimaryAndUserSwitcherIsDisabled() { + createUserInteractor() testScope.runTest { val userInfos = createUserInfos(count = 2, includeGuest = false) userRepository.setUserInfos(userInfos) @@ -869,9 +933,11 @@ class UserInteractorTest : SysuiTestCase() { val selectedUser = collectLastValue(underTest.selectedUser) assertThat(selectedUser()).isNotNull() } + } @Test - fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() = + fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() { + createUserInteractor() testScope.runTest { keyguardRepository.setKeyguardShowing(true) whenever(manager.getUserSwitchability(any())) @@ -891,9 +957,11 @@ class UserInteractorTest : SysuiTestCase() { .filter { it.info == null } .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() } } + } @Test - fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() = + fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() { + createUserInteractor() testScope.runTest { keyguardRepository.setKeyguardShowing(true) whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true) @@ -913,6 +981,7 @@ class UserInteractorTest : SysuiTestCase() { .filter { it.info == null } .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() } } + } private fun assertUsers( models: List?, @@ -1005,6 +1074,53 @@ class UserInteractorTest : SysuiTestCase() { .isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER) } + private fun createUserInteractor(startAsProcessUser: Boolean = true) { + val processUserId = Process.myUserHandle().identifier + val startUserId = if (startAsProcessUser) processUserId else (processUserId + 1) + runBlocking { + val userInfo = + createUserInfo(id = startUserId, name = "user_$startUserId", isPrimary = true) + userRepository.setUserInfos(listOf(userInfo)) + userRepository.setSelectedUserInfo(userInfo) + } + underTest = + UserInteractor( + applicationContext = spyContext, + repository = userRepository, + activityStarter = activityStarter, + keyguardInteractor = keyguardReply.keyguardInteractor, + manager = manager, + headlessSystemUserMode = headlessSystemUserMode, + applicationScope = testScope.backgroundScope, + telephonyInteractor = + TelephonyInteractor( + repository = telephonyRepository, + ), + broadcastDispatcher = fakeBroadcastDispatcher, + keyguardUpdateMonitor = keyguardUpdateMonitor, + backgroundDispatcher = testDispatcher, + activityManager = activityManager, + refreshUsersScheduler = refreshUsersScheduler, + guestUserInteractor = + GuestUserInteractor( + applicationContext = spyContext, + applicationScope = testScope.backgroundScope, + mainDispatcher = testDispatcher, + backgroundDispatcher = testDispatcher, + manager = manager, + repository = userRepository, + deviceProvisionedController = deviceProvisionedController, + devicePolicyManager = devicePolicyManager, + refreshUsersScheduler = refreshUsersScheduler, + uiEventLogger = uiEventLogger, + resumeSessionReceiver = resumeSessionReceiver, + resetOrExitSessionReceiver = resetOrExitSessionReceiver, + ), + uiEventLogger = uiEventLogger, + featureFlags = featureFlags, + ) + } + private fun createUserInfos( count: Int, includeGuest: Boolean, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index 9cb26e05887df..2433e123a309f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -50,6 +50,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle @@ -237,6 +238,10 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.FACE_AUTH_REFACTOR, true) } + runBlocking { + userRepository.setUserInfos(listOf(USER_0)) + userRepository.setSelectedUserInfo(USER_0) + } return StatusBarUserChipViewModel( context = context, interactor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index e3f9fac278155..8c88f95d73a5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -102,6 +102,20 @@ class UserSwitcherViewModelTest : SysuiTestCase() { testScope = TestScope(testDispatcher) userRepository = FakeUserRepository() runBlocking { + val userInfos = + listOf( + UserInfo( + /* id= */ 0, + /* name= */ "zero", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_PRIMARY or + UserInfo.FLAG_ADMIN or + UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + ) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) userRepository.setSettings( UserSwitcherSettingsModel( isUserSwitcherEnabled = true,