1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 package com.android.systemui.user.domain.interactor
19 
20 import android.app.ActivityManager
21 import android.app.admin.DevicePolicyManager
22 import android.content.Context
23 import android.content.Intent
24 import android.content.pm.UserInfo
25 import android.graphics.Bitmap
26 import android.graphics.drawable.Drawable
27 import android.os.Process
28 import android.os.UserHandle
29 import android.os.UserManager
30 import android.provider.Settings
31 import androidx.test.filters.SmallTest
32 import com.android.internal.logging.UiEventLogger
33 import com.android.keyguard.KeyguardUpdateMonitor
34 import com.android.keyguard.KeyguardUpdateMonitorCallback
35 import com.android.systemui.GuestResetOrExitSessionReceiver
36 import com.android.systemui.GuestResumeSessionReceiver
37 import com.android.systemui.R
38 import com.android.systemui.SysuiTestCase
39 import com.android.systemui.animation.Expandable
40 import com.android.systemui.common.shared.model.Text
41 import com.android.systemui.coroutines.collectLastValue
42 import com.android.systemui.flags.FakeFeatureFlags
43 import com.android.systemui.flags.Flags
44 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
45 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
46 import com.android.systemui.plugins.ActivityStarter
47 import com.android.systemui.qs.user.UserSwitchDialogController
48 import com.android.systemui.statusbar.policy.DeviceProvisionedController
49 import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
50 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
51 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
52 import com.android.systemui.user.data.repository.FakeUserRepository
53 import com.android.systemui.user.data.source.UserRecord
54 import com.android.systemui.user.domain.model.ShowDialogRequestModel
55 import com.android.systemui.user.shared.model.UserActionModel
56 import com.android.systemui.user.shared.model.UserModel
57 import com.android.systemui.user.utils.MultiUserActionsEvent
58 import com.android.systemui.util.mockito.any
59 import com.android.systemui.util.mockito.eq
60 import com.android.systemui.util.mockito.kotlinArgumentCaptor
61 import com.android.systemui.util.mockito.mock
62 import com.android.systemui.util.mockito.whenever
63 import com.google.common.truth.Truth.assertThat
64 import junit.framework.Assert.assertNotNull
65 import kotlinx.coroutines.ExperimentalCoroutinesApi
66 import kotlinx.coroutines.launch
67 import kotlinx.coroutines.runBlocking
68 import kotlinx.coroutines.test.StandardTestDispatcher
69 import kotlinx.coroutines.test.TestDispatcher
70 import kotlinx.coroutines.test.TestScope
71 import kotlinx.coroutines.test.runCurrent
72 import kotlinx.coroutines.test.runTest
73 import org.junit.Before
74 import org.junit.Test
75 import org.junit.runner.RunWith
76 import org.junit.runners.JUnit4
77 import org.mockito.ArgumentCaptor
78 import org.mockito.ArgumentMatchers.anyBoolean
79 import org.mockito.ArgumentMatchers.anyInt
80 import org.mockito.Mock
81 import org.mockito.Mockito.atLeastOnce
82 import org.mockito.Mockito.never
83 import org.mockito.Mockito.spy
84 import org.mockito.Mockito.times
85 import org.mockito.Mockito.verify
86 import org.mockito.MockitoAnnotations
87 
88 @OptIn(ExperimentalCoroutinesApi::class)
89 @SmallTest
90 @RunWith(JUnit4::class)
91 class UserInteractorTest : SysuiTestCase() {
92 
93     @Mock private lateinit var activityStarter: ActivityStarter
94     @Mock private lateinit var manager: UserManager
95     @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
96     @Mock private lateinit var activityManager: ActivityManager
97     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
98     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
99     @Mock private lateinit var uiEventLogger: UiEventLogger
100     @Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
101     @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
102     @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
103     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
104 
105     private lateinit var underTest: UserInteractor
106 
107     private lateinit var spyContext: Context
108     private lateinit var testScope: TestScope
109     private lateinit var userRepository: FakeUserRepository
110     private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies
111     private lateinit var keyguardRepository: FakeKeyguardRepository
112     private lateinit var telephonyRepository: FakeTelephonyRepository
113     private lateinit var testDispatcher: TestDispatcher
114     private lateinit var featureFlags: FakeFeatureFlags
115     private lateinit var refreshUsersScheduler: RefreshUsersScheduler
116 
117     @Before
118     fun setUp() {
119         MockitoAnnotations.initMocks(this)
120         whenever(manager.getUserIcon(anyInt())).thenReturn(ICON)
121         whenever(manager.canAddMoreUsers(any())).thenReturn(true)
122 
123         overrideResource(R.drawable.ic_account_circle, GUEST_ICON)
124         overrideResource(R.dimen.max_avatar_size, 10)
125         overrideResource(
126             com.android.internal.R.string.config_supervisedUserCreationPackage,
127             SUPERVISED_USER_CREATION_APP_PACKAGE,
128         )
129 
130         featureFlags =
131             FakeFeatureFlags().apply {
132                 set(Flags.FULL_SCREEN_USER_SWITCHER, false)
133                 set(Flags.FACE_AUTH_REFACTOR, true)
134             }
135         spyContext = spy(context)
136         keyguardReply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
137         keyguardRepository = keyguardReply.repository
138         userRepository = FakeUserRepository()
139         telephonyRepository = FakeTelephonyRepository()
140         testDispatcher = StandardTestDispatcher()
141         testScope = TestScope(testDispatcher)
142         refreshUsersScheduler =
143             RefreshUsersScheduler(
144                 applicationScope = testScope.backgroundScope,
145                 mainDispatcher = testDispatcher,
146                 repository = userRepository,
147             )
148     }
149 
150     @Test
151     fun createUserInteractor_processUser_noSecondaryService() {
152         createUserInteractor()
153         verify(spyContext, never()).startServiceAsUser(any(), any())
154     }
155 
156     @Test
157     fun createUserInteractor_nonProcessUser_startsSecondaryService() {
158         val userId = Process.myUserHandle().identifier + 1
159         whenever(manager.aliveUsers).thenReturn(listOf(createUserInfo(userId, "abc")))
160 
161         createUserInteractor(false /* startAsProcessUser */)
162         verify(spyContext).startServiceAsUser(any(), any())
163     }
164 
165     @Test
166     fun testKeyguardUpdateMonitor_onKeyguardGoingAway() {
167         createUserInteractor()
168         testScope.runTest {
169             val argumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
170             verify(keyguardUpdateMonitor).registerCallback(argumentCaptor.capture())
171 
172             argumentCaptor.value.onKeyguardGoingAway()
173 
174             val lastValue = collectLastValue(underTest.dialogDismissRequests)
175             assertNotNull(lastValue)
176         }
177     }
178 
179     @Test
180     fun onRecordSelected_user() {
181         createUserInteractor()
182         testScope.runTest {
183             val userInfos = createUserInfos(count = 3, includeGuest = false)
184             userRepository.setUserInfos(userInfos)
185             userRepository.setSelectedUserInfo(userInfos[0])
186             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
187 
188             underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower)
189 
190             verify(uiEventLogger, times(1))
191                 .log(MultiUserActionsEvent.SWITCH_TO_USER_FROM_USER_SWITCHER)
192             verify(dialogShower).dismiss()
193             verify(activityManager).switchUser(userInfos[1].id)
194             Unit
195         }
196     }
197 
198     @Test
199     fun onRecordSelected_switchToGuestUser() {
200         createUserInteractor()
201         testScope.runTest {
202             val userInfos = createUserInfos(count = 3, includeGuest = true)
203             userRepository.setUserInfos(userInfos)
204             userRepository.setSelectedUserInfo(userInfos[0])
205             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
206 
207             underTest.onRecordSelected(UserRecord(info = userInfos.last()))
208 
209             verify(uiEventLogger, times(1))
210                 .log(MultiUserActionsEvent.SWITCH_TO_GUEST_FROM_USER_SWITCHER)
211             verify(activityManager).switchUser(userInfos.last().id)
212             Unit
213         }
214     }
215 
216     @Test
217     fun onRecordSelected_switchToRestrictedUser() {
218         createUserInteractor()
219         testScope.runTest {
220             var userInfos = createUserInfos(count = 2, includeGuest = false).toMutableList()
221             userInfos.add(
222                 UserInfo(
223                     60,
224                     "Restricted user",
225                     /* iconPath= */ "",
226                     /* flags= */ UserInfo.FLAG_FULL,
227                     UserManager.USER_TYPE_FULL_RESTRICTED,
228                 )
229             )
230             userRepository.setUserInfos(userInfos)
231             userRepository.setSelectedUserInfo(userInfos[0])
232             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
233 
234             underTest.onRecordSelected(UserRecord(info = userInfos.last()))
235 
236             verify(uiEventLogger, times(1))
237                 .log(MultiUserActionsEvent.SWITCH_TO_RESTRICTED_USER_FROM_USER_SWITCHER)
238             verify(activityManager).switchUser(userInfos.last().id)
239             Unit
240         }
241     }
242 
243     @Test
244     fun onRecordSelected_enterGuestMode() {
245         createUserInteractor()
246         testScope.runTest {
247             val userInfos = createUserInfos(count = 3, includeGuest = false)
248             userRepository.setUserInfos(userInfos)
249             userRepository.setSelectedUserInfo(userInfos[0])
250             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
251             val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
252             whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
253 
254             underTest.onRecordSelected(UserRecord(isGuest = true), dialogShower)
255             runCurrent()
256 
257             verify(uiEventLogger, times(1))
258                 .log(MultiUserActionsEvent.CREATE_GUEST_FROM_USER_SWITCHER)
259             verify(dialogShower).dismiss()
260             verify(manager).createGuest(any())
261             Unit
262         }
263     }
264 
265     @Test
266     fun onRecordSelected_action() {
267         createUserInteractor()
268         testScope.runTest {
269             val userInfos = createUserInfos(count = 3, includeGuest = true)
270             userRepository.setUserInfos(userInfos)
271             userRepository.setSelectedUserInfo(userInfos[0])
272             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
273 
274             underTest.onRecordSelected(UserRecord(isAddSupervisedUser = true), dialogShower)
275 
276             verify(uiEventLogger, times(1))
277                 .log(MultiUserActionsEvent.CREATE_RESTRICTED_USER_FROM_USER_SWITCHER)
278             verify(dialogShower, never()).dismiss()
279             verify(activityStarter).startActivity(any(), anyBoolean())
280         }
281     }
282 
283     @Test
284     fun users_switcherEnabled() {
285         createUserInteractor()
286         testScope.runTest {
287             val userInfos = createUserInfos(count = 3, includeGuest = true)
288             userRepository.setUserInfos(userInfos)
289             userRepository.setSelectedUserInfo(userInfos[0])
290             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
291 
292             val value = collectLastValue(underTest.users)
293 
294             assertUsers(models = value(), count = 3, includeGuest = true)
295         }
296     }
297 
298     @Test
299     fun users_switchesToSecondUser() {
300         createUserInteractor()
301         testScope.runTest {
302             val userInfos = createUserInfos(count = 2, includeGuest = false)
303             userRepository.setUserInfos(userInfos)
304             userRepository.setSelectedUserInfo(userInfos[0])
305             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
306 
307             val value = collectLastValue(underTest.users)
308             userRepository.setSelectedUserInfo(userInfos[1])
309 
310             assertUsers(models = value(), count = 2, selectedIndex = 1)
311         }
312     }
313 
314     @Test
315     fun users_switcherNotEnabled() {
316         createUserInteractor()
317         testScope.runTest {
318             val userInfos = createUserInfos(count = 2, includeGuest = false)
319             userRepository.setUserInfos(userInfos)
320             userRepository.setSelectedUserInfo(userInfos[0])
321             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
322 
323             val value = collectLastValue(underTest.users)
324             assertUsers(models = value(), count = 1)
325         }
326     }
327 
328     @Test
329     fun selectedUser() {
330         createUserInteractor()
331         testScope.runTest {
332             val userInfos = createUserInfos(count = 2, includeGuest = false)
333             userRepository.setUserInfos(userInfos)
334             userRepository.setSelectedUserInfo(userInfos[0])
335             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
336 
337             val value = collectLastValue(underTest.selectedUser)
338             assertUser(value(), id = 0, isSelected = true)
339 
340             userRepository.setSelectedUserInfo(userInfos[1])
341             assertUser(value(), id = 1, isSelected = true)
342         }
343     }
344 
345     @Test
346     fun actions_deviceUnlocked() {
347         createUserInteractor()
348         testScope.runTest {
349             val userInfos = createUserInfos(count = 2, includeGuest = false)
350 
351             userRepository.setUserInfos(userInfos)
352             userRepository.setSelectedUserInfo(userInfos[0])
353             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
354             keyguardRepository.setKeyguardShowing(false)
355             val value = collectLastValue(underTest.actions)
356 
357             runCurrent()
358 
359             assertThat(value())
360                 .isEqualTo(
361                     listOf(
362                         UserActionModel.ENTER_GUEST_MODE,
363                         UserActionModel.ADD_USER,
364                         UserActionModel.ADD_SUPERVISED_USER,
365                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
366                     )
367                 )
368         }
369     }
370 
371     @Test
372     fun actions_deviceUnlocked_fullScreen() {
373         createUserInteractor()
374         testScope.runTest {
375             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
376             val userInfos = createUserInfos(count = 2, includeGuest = false)
377 
378             userRepository.setUserInfos(userInfos)
379             userRepository.setSelectedUserInfo(userInfos[0])
380             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
381             keyguardRepository.setKeyguardShowing(false)
382             val value = collectLastValue(underTest.actions)
383 
384             assertThat(value())
385                 .isEqualTo(
386                     listOf(
387                         UserActionModel.ADD_USER,
388                         UserActionModel.ADD_SUPERVISED_USER,
389                         UserActionModel.ENTER_GUEST_MODE,
390                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
391                     )
392                 )
393         }
394     }
395 
396     @Test
397     fun actions_deviceUnlockedUserNotPrimary_emptyList() {
398         createUserInteractor()
399         testScope.runTest {
400             val userInfos = createUserInfos(count = 2, includeGuest = false)
401             userRepository.setUserInfos(userInfos)
402             userRepository.setSelectedUserInfo(userInfos[1])
403             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
404             keyguardRepository.setKeyguardShowing(false)
405             val value = collectLastValue(underTest.actions)
406 
407             assertThat(value()).isEqualTo(emptyList<UserActionModel>())
408         }
409     }
410 
411     @Test
412     fun actions_deviceUnlockedUserIsGuest_emptyList() {
413         createUserInteractor()
414         testScope.runTest {
415             val userInfos = createUserInfos(count = 2, includeGuest = true)
416             assertThat(userInfos[1].isGuest).isTrue()
417             userRepository.setUserInfos(userInfos)
418             userRepository.setSelectedUserInfo(userInfos[1])
419             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
420             keyguardRepository.setKeyguardShowing(false)
421             val value = collectLastValue(underTest.actions)
422 
423             assertThat(value()).isEqualTo(emptyList<UserActionModel>())
424         }
425     }
426 
427     @Test
428     fun actions_deviceLockedAddFromLockscreenSet_fullList() {
429         createUserInteractor()
430         testScope.runTest {
431             val userInfos = createUserInfos(count = 2, includeGuest = false)
432             userRepository.setUserInfos(userInfos)
433             userRepository.setSelectedUserInfo(userInfos[0])
434             userRepository.setSettings(
435                 UserSwitcherSettingsModel(
436                     isUserSwitcherEnabled = true,
437                     isAddUsersFromLockscreen = true,
438                 )
439             )
440             keyguardRepository.setKeyguardShowing(false)
441             val value = collectLastValue(underTest.actions)
442 
443             assertThat(value())
444                 .isEqualTo(
445                     listOf(
446                         UserActionModel.ENTER_GUEST_MODE,
447                         UserActionModel.ADD_USER,
448                         UserActionModel.ADD_SUPERVISED_USER,
449                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
450                     )
451                 )
452         }
453     }
454 
455     @Test
456     fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() {
457         createUserInteractor()
458         testScope.runTest {
459             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
460             val userInfos = createUserInfos(count = 2, includeGuest = false)
461             userRepository.setUserInfos(userInfos)
462             userRepository.setSelectedUserInfo(userInfos[0])
463             userRepository.setSettings(
464                 UserSwitcherSettingsModel(
465                     isUserSwitcherEnabled = true,
466                     isAddUsersFromLockscreen = true,
467                 )
468             )
469             keyguardRepository.setKeyguardShowing(false)
470             val value = collectLastValue(underTest.actions)
471 
472             assertThat(value())
473                 .isEqualTo(
474                     listOf(
475                         UserActionModel.ADD_USER,
476                         UserActionModel.ADD_SUPERVISED_USER,
477                         UserActionModel.ENTER_GUEST_MODE,
478                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
479                     )
480                 )
481         }
482     }
483 
484     @Test
485     fun actions_deviceLocked_onlymanageUserIsShown() {
486         createUserInteractor()
487         testScope.runTest {
488             val userInfos = createUserInfos(count = 2, includeGuest = false)
489             userRepository.setUserInfos(userInfos)
490             userRepository.setSelectedUserInfo(userInfos[0])
491             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
492             keyguardRepository.setKeyguardShowing(true)
493             val value = collectLastValue(underTest.actions)
494 
495             assertThat(value()).isEqualTo(listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT))
496         }
497     }
498 
499     @Test
500     fun executeAction_addUser_dismissesDialogAndStartsActivity() {
501         createUserInteractor()
502         testScope.runTest {
503             val userInfos = createUserInfos(count = 2, includeGuest = false)
504             userRepository.setUserInfos(userInfos)
505             userRepository.setSelectedUserInfo(userInfos[0])
506             keyguardRepository.setKeyguardShowing(false)
507 
508             underTest.executeAction(UserActionModel.ADD_USER)
509             verify(uiEventLogger, times(1))
510                 .log(MultiUserActionsEvent.CREATE_USER_FROM_USER_SWITCHER)
511             underTest.onDialogShown()
512         }
513     }
514 
515     @Test
516     fun executeAction_addSupervisedUser_dismissesDialogAndStartsActivity() {
517         createUserInteractor()
518         testScope.runTest {
519             underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
520 
521             verify(uiEventLogger, times(1))
522                 .log(MultiUserActionsEvent.CREATE_RESTRICTED_USER_FROM_USER_SWITCHER)
523             val intentCaptor = kotlinArgumentCaptor<Intent>()
524             verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
525             assertThat(intentCaptor.value.action)
526                 .isEqualTo(UserManager.ACTION_CREATE_SUPERVISED_USER)
527             assertThat(intentCaptor.value.`package`).isEqualTo(SUPERVISED_USER_CREATION_APP_PACKAGE)
528         }
529     }
530 
531     @Test
532     fun executeAction_navigateToManageUsers() {
533         createUserInteractor()
534         testScope.runTest {
535             underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
536 
537             val intentCaptor = kotlinArgumentCaptor<Intent>()
538             verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
539             assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
540         }
541     }
542 
543     @Test
544     fun executeAction_guestMode() {
545         createUserInteractor()
546         testScope.runTest {
547             val userInfos = createUserInfos(count = 2, includeGuest = false)
548             userRepository.setUserInfos(userInfos)
549             userRepository.setSelectedUserInfo(userInfos[0])
550             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
551             val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
552             whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
553             val dialogRequests = mutableListOf<ShowDialogRequestModel?>()
554             backgroundScope.launch {
555                 underTest.dialogShowRequests.collect {
556                     dialogRequests.add(it)
557                     if (it != null) {
558                         underTest.onDialogShown()
559                     }
560                 }
561             }
562             backgroundScope.launch {
563                 underTest.dialogDismissRequests.collect {
564                     if (it != null) {
565                         underTest.onDialogDismissed()
566                     }
567                 }
568             }
569 
570             underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
571             runCurrent()
572 
573             verify(uiEventLogger, times(1))
574                 .log(MultiUserActionsEvent.CREATE_GUEST_FROM_USER_SWITCHER)
575             assertThat(dialogRequests)
576                 .contains(
577                     ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true),
578                 )
579             verify(activityManager).switchUser(guestUserInfo.id)
580         }
581     }
582 
583     @Test
584     fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() {
585         createUserInteractor()
586         testScope.runTest {
587             val userInfos = createUserInfos(count = 2, includeGuest = true)
588             val guestUserInfo = userInfos[1]
589             assertThat(guestUserInfo.isGuest).isTrue()
590             userRepository.setUserInfos(userInfos)
591             userRepository.setSelectedUserInfo(guestUserInfo)
592             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
593             val dialogRequest = collectLastValue(underTest.dialogShowRequests)
594 
595             underTest.selectUser(
596                 newlySelectedUserId = guestUserInfo.id,
597                 dialogShower = dialogShower,
598             )
599 
600             assertThat(dialogRequest())
601                 .isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
602             verify(dialogShower, never()).dismiss()
603         }
604     }
605 
606     @Test
607     fun selectUser_currentlyGuestNonGuestSelected_exitGuestDialog() {
608         createUserInteractor()
609         testScope.runTest {
610             val userInfos = createUserInfos(count = 2, includeGuest = true)
611             val guestUserInfo = userInfos[1]
612             assertThat(guestUserInfo.isGuest).isTrue()
613             userRepository.setUserInfos(userInfos)
614             userRepository.setSelectedUserInfo(guestUserInfo)
615             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
616             val dialogRequest = collectLastValue(underTest.dialogShowRequests)
617 
618             underTest.selectUser(newlySelectedUserId = userInfos[0].id, dialogShower = dialogShower)
619 
620             assertThat(dialogRequest())
621                 .isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
622             verify(dialogShower, never()).dismiss()
623         }
624     }
625 
626     @Test
627     fun selectUser_notCurrentlyGuest_switchesUsers() {
628         createUserInteractor()
629         testScope.runTest {
630             val userInfos = createUserInfos(count = 2, includeGuest = false)
631             userRepository.setUserInfos(userInfos)
632             userRepository.setSelectedUserInfo(userInfos[0])
633             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
634             val dialogRequest = collectLastValue(underTest.dialogShowRequests)
635 
636             underTest.selectUser(newlySelectedUserId = userInfos[1].id, dialogShower = dialogShower)
637 
638             assertThat(dialogRequest()).isNull()
639             verify(activityManager).switchUser(userInfos[1].id)
640             verify(dialogShower).dismiss()
641         }
642     }
643 
644     @Test
645     fun telephonyCallStateChanges_refreshesUsers() {
646         createUserInteractor()
647         testScope.runTest {
648             runCurrent()
649 
650             val refreshUsersCallCount = userRepository.refreshUsersCallCount
651 
652             telephonyRepository.setCallState(1)
653             runCurrent()
654 
655             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
656         }
657     }
658 
659     @Test
660     fun userSwitchedBroadcast() {
661         testScope.runTest {
662             val userInfos = createUserInfos(count = 2, includeGuest = false)
663             whenever(manager.aliveUsers).thenReturn(userInfos)
664             createUserInteractor()
665             userRepository.setUserInfos(userInfos)
666             userRepository.setSelectedUserInfo(userInfos[0])
667             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
668             val callback1: UserInteractor.UserCallback = mock()
669             val callback2: UserInteractor.UserCallback = mock()
670             underTest.addCallback(callback1)
671             underTest.addCallback(callback2)
672             runCurrent()
673             val refreshUsersCallCount = userRepository.refreshUsersCallCount
674 
675             userRepository.setSelectedUserInfo(userInfos[1])
676             runCurrent()
677             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
678                 spyContext,
679                 Intent(Intent.ACTION_USER_SWITCHED)
680                     .putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id),
681             )
682             runCurrent()
683 
684             verify(callback1, atLeastOnce()).onUserStateChanged()
685             verify(callback2, atLeastOnce()).onUserStateChanged()
686             assertThat(userRepository.secondaryUserId).isEqualTo(userInfos[1].id)
687             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
688             verify(spyContext).startServiceAsUser(any(), eq(UserHandle.of(userInfos[1].id)))
689         }
690     }
691 
692     @Test
693     fun userInfoChangedBroadcast() {
694         createUserInteractor()
695         testScope.runTest {
696             val userInfos = createUserInfos(count = 2, includeGuest = false)
697             userRepository.setUserInfos(userInfos)
698             userRepository.setSelectedUserInfo(userInfos[0])
699             runCurrent()
700             val refreshUsersCallCount = userRepository.refreshUsersCallCount
701 
702             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
703                 spyContext,
704                 Intent(Intent.ACTION_USER_INFO_CHANGED),
705             )
706 
707             runCurrent()
708 
709             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
710         }
711     }
712 
713     @Test
714     fun systemUserUnlockedBroadcast_refreshUsers() {
715         createUserInteractor()
716         testScope.runTest {
717             val userInfos = createUserInfos(count = 2, includeGuest = false)
718             userRepository.setUserInfos(userInfos)
719             userRepository.setSelectedUserInfo(userInfos[0])
720             runCurrent()
721             val refreshUsersCallCount = userRepository.refreshUsersCallCount
722 
723             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
724                 spyContext,
725                 Intent(Intent.ACTION_USER_UNLOCKED)
726                     .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM),
727             )
728             runCurrent()
729 
730             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
731         }
732     }
733 
734     @Test
735     fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() {
736         createUserInteractor()
737         testScope.runTest {
738             val userInfos = createUserInfos(count = 2, includeGuest = false)
739             userRepository.setUserInfos(userInfos)
740             userRepository.setSelectedUserInfo(userInfos[0])
741             val refreshUsersCallCount = userRepository.refreshUsersCallCount
742 
743             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
744                 spyContext,
745                 Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337),
746             )
747 
748             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount)
749         }
750     }
751 
752     @Test
753     fun userRecords() {
754         createUserInteractor()
755         testScope.runTest {
756             val userInfos = createUserInfos(count = 3, includeGuest = false)
757             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
758             userRepository.setUserInfos(userInfos)
759             userRepository.setSelectedUserInfo(userInfos[0])
760             keyguardRepository.setKeyguardShowing(false)
761 
762             runCurrent()
763 
764             assertRecords(
765                 records = underTest.userRecords.value,
766                 userIds = listOf(0, 1, 2),
767                 selectedUserIndex = 0,
768                 includeGuest = false,
769                 expectedActions =
770                     listOf(
771                         UserActionModel.ENTER_GUEST_MODE,
772                         UserActionModel.ADD_USER,
773                         UserActionModel.ADD_SUPERVISED_USER,
774                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
775                     ),
776             )
777         }
778     }
779 
780     @Test
781     fun userRecordsFullScreen() {
782         createUserInteractor()
783         testScope.runTest {
784             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
785             val userInfos = createUserInfos(count = 3, includeGuest = false)
786             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
787             userRepository.setUserInfos(userInfos)
788             userRepository.setSelectedUserInfo(userInfos[0])
789             keyguardRepository.setKeyguardShowing(false)
790 
791             runCurrent()
792 
793             assertRecords(
794                 records = underTest.userRecords.value,
795                 userIds = listOf(0, 1, 2),
796                 selectedUserIndex = 0,
797                 includeGuest = false,
798                 expectedActions =
799                     listOf(
800                         UserActionModel.ADD_USER,
801                         UserActionModel.ADD_SUPERVISED_USER,
802                         UserActionModel.ENTER_GUEST_MODE,
803                         UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
804                     ),
805             )
806         }
807     }
808 
809     @Test
810     fun selectedUserRecord() {
811         createUserInteractor()
812         testScope.runTest {
813             val userInfos = createUserInfos(count = 3, includeGuest = true)
814             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
815             userRepository.setUserInfos(userInfos)
816             userRepository.setSelectedUserInfo(userInfos[0])
817             keyguardRepository.setKeyguardShowing(false)
818 
819             assertRecordForUser(
820                 record = underTest.selectedUserRecord.value,
821                 id = 0,
822                 hasPicture = true,
823                 isCurrent = true,
824                 isSwitchToEnabled = true,
825             )
826         }
827     }
828 
829     @Test
830     fun users_secondaryUser_guestUserCanBeSwitchedTo() {
831         createUserInteractor()
832         testScope.runTest {
833             val userInfos = createUserInfos(count = 3, includeGuest = true)
834             userRepository.setUserInfos(userInfos)
835             userRepository.setSelectedUserInfo(userInfos[1])
836             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
837 
838             val res = collectLastValue(underTest.users)
839             assertThat(res()?.size == 3).isTrue()
840             assertThat(res()?.find { it.isGuest }).isNotNull()
841         }
842     }
843 
844     @Test
845     fun users_secondaryUser_noGuestAction() {
846         createUserInteractor()
847         testScope.runTest {
848             val userInfos = createUserInfos(count = 3, includeGuest = true)
849             userRepository.setUserInfos(userInfos)
850             userRepository.setSelectedUserInfo(userInfos[1])
851             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
852 
853             val res = collectLastValue(underTest.actions)
854             assertThat(res()?.find { it == UserActionModel.ENTER_GUEST_MODE }).isNull()
855         }
856     }
857 
858     @Test
859     fun users_secondaryUser_noGuestUserRecord() {
860         createUserInteractor()
861         testScope.runTest {
862             val userInfos = createUserInfos(count = 3, includeGuest = true)
863             userRepository.setUserInfos(userInfos)
864             userRepository.setSelectedUserInfo(userInfos[1])
865             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
866 
867             assertThat(underTest.userRecords.value.find { it.isGuest }).isNull()
868         }
869     }
870 
871     @Test
872     fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() {
873         createUserInteractor()
874         testScope.runTest {
875             val expandable = mock<Expandable>()
876             underTest.showUserSwitcher(expandable)
877 
878             val dialogRequest = collectLastValue(underTest.dialogShowRequests)
879 
880             // Dialog is shown.
881             assertThat(dialogRequest())
882                 .isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable))
883 
884             underTest.onDialogShown()
885             assertThat(dialogRequest()).isNull()
886         }
887     }
888 
889     @Test
890     fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() {
891         createUserInteractor()
892         testScope.runTest {
893             featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
894 
895             val expandable = mock<Expandable>()
896             underTest.showUserSwitcher(expandable)
897 
898             val dialogRequest = collectLastValue(underTest.dialogShowRequests)
899 
900             // Dialog is shown.
901             assertThat(dialogRequest())
902                 .isEqualTo(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable))
903 
904             underTest.onDialogShown()
905             assertThat(dialogRequest()).isNull()
906         }
907     }
908 
909     @Test
910     fun users_secondaryUser_managedProfileIsNotIncluded() {
911         createUserInteractor()
912         testScope.runTest {
913             val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
914             userInfos.add(
915                 UserInfo(
916                     50,
917                     "Work Profile",
918                     /* iconPath= */ "",
919                     /* flags= */ UserInfo.FLAG_MANAGED_PROFILE
920                 )
921             )
922             userRepository.setUserInfos(userInfos)
923             userRepository.setSelectedUserInfo(userInfos[1])
924             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
925 
926             val res = collectLastValue(underTest.users)
927             assertThat(res()?.size == 3).isTrue()
928         }
929     }
930 
931     @Test
932     fun currentUserIsNotPrimaryAndUserSwitcherIsDisabled() {
933         createUserInteractor()
934         testScope.runTest {
935             val userInfos = createUserInfos(count = 2, includeGuest = false)
936             userRepository.setUserInfos(userInfos)
937             userRepository.setSelectedUserInfo(userInfos[1])
938             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
939             val selectedUser = collectLastValue(underTest.selectedUser)
940             assertThat(selectedUser()).isNotNull()
941         }
942     }
943 
944     @Test
945     fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() {
946         createUserInteractor()
947         testScope.runTest {
948             keyguardRepository.setKeyguardShowing(true)
949             whenever(manager.getUserSwitchability(any()))
950                 .thenReturn(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED)
951             val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
952             userRepository.setUserInfos(userInfos)
953             userRepository.setSelectedUserInfo(userInfos[1])
954             userRepository.setSettings(
955                 UserSwitcherSettingsModel(
956                     isUserSwitcherEnabled = true,
957                     isAddUsersFromLockscreen = true
958                 )
959             )
960 
961             runCurrent()
962             underTest.userRecords.value
963                 .filter { it.info == null }
964                 .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
965         }
966     }
967 
968     @Test
969     fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() {
970         createUserInteractor()
971         testScope.runTest {
972             keyguardRepository.setKeyguardShowing(true)
973             whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true)
974             whenever(manager.isUserUnlocked(anyInt())).thenReturn(false)
975             val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
976             userRepository.setUserInfos(userInfos)
977             userRepository.setSelectedUserInfo(userInfos[1])
978             userRepository.setSettings(
979                 UserSwitcherSettingsModel(
980                     isUserSwitcherEnabled = true,
981                     isAddUsersFromLockscreen = true
982                 )
983             )
984 
985             runCurrent()
986             underTest.userRecords.value
987                 .filter { it.info == null }
988                 .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
989         }
990     }
991 
992     @Test
993     fun initWithNoAliveUsers() {
994         whenever(manager.aliveUsers).thenReturn(listOf())
995         createUserInteractor()
996         verify(spyContext, never()).startServiceAsUser(any(), any())
997     }
998 
999     private fun assertUsers(
1000         models: List<UserModel>?,
1001         count: Int,
1002         selectedIndex: Int = 0,
1003         includeGuest: Boolean = false,
1004     ) {
1005         checkNotNull(models)
1006         assertThat(models.size).isEqualTo(count)
1007         models.forEachIndexed { index, model ->
1008             assertUser(
1009                 model = model,
1010                 id = index,
1011                 isSelected = index == selectedIndex,
1012                 isGuest = includeGuest && index == count - 1
1013             )
1014         }
1015     }
1016 
1017     private fun assertUser(
1018         model: UserModel?,
1019         id: Int,
1020         isSelected: Boolean = false,
1021         isGuest: Boolean = false,
1022     ) {
1023         checkNotNull(model)
1024         assertThat(model.id).isEqualTo(id)
1025         assertThat(model.name).isEqualTo(Text.Loaded(if (isGuest) "guest" else "user_$id"))
1026         assertThat(model.isSelected).isEqualTo(isSelected)
1027         assertThat(model.isSelectable).isTrue()
1028         assertThat(model.isGuest).isEqualTo(isGuest)
1029     }
1030 
1031     private fun assertRecords(
1032         records: List<UserRecord>,
1033         userIds: List<Int>,
1034         selectedUserIndex: Int = 0,
1035         includeGuest: Boolean = false,
1036         expectedActions: List<UserActionModel> = emptyList(),
1037     ) {
1038         assertThat(records.size >= userIds.size).isTrue()
1039         userIds.indices.forEach { userIndex ->
1040             val record = records[userIndex]
1041             assertThat(record.info).isNotNull()
1042             val isGuest = includeGuest && userIndex == userIds.size - 1
1043             assertRecordForUser(
1044                 record = record,
1045                 id = userIds[userIndex],
1046                 hasPicture = !isGuest,
1047                 isCurrent = userIndex == selectedUserIndex,
1048                 isGuest = isGuest,
1049                 isSwitchToEnabled = true,
1050             )
1051         }
1052 
1053         assertThat(records.size - userIds.size).isEqualTo(expectedActions.size)
1054         (userIds.size until userIds.size + expectedActions.size).forEach { actionIndex ->
1055             val record = records[actionIndex]
1056             assertThat(record.info).isNull()
1057             assertRecordForAction(
1058                 record = record,
1059                 type = expectedActions[actionIndex - userIds.size],
1060             )
1061         }
1062     }
1063 
1064     private fun assertRecordForUser(
1065         record: UserRecord?,
1066         id: Int? = null,
1067         hasPicture: Boolean = false,
1068         isCurrent: Boolean = false,
1069         isGuest: Boolean = false,
1070         isSwitchToEnabled: Boolean = false,
1071     ) {
1072         checkNotNull(record)
1073         assertThat(record.info?.id).isEqualTo(id)
1074         assertThat(record.picture != null).isEqualTo(hasPicture)
1075         assertThat(record.isCurrent).isEqualTo(isCurrent)
1076         assertThat(record.isGuest).isEqualTo(isGuest)
1077         assertThat(record.isSwitchToEnabled).isEqualTo(isSwitchToEnabled)
1078     }
1079 
1080     private fun assertRecordForAction(
1081         record: UserRecord,
1082         type: UserActionModel,
1083     ) {
1084         assertThat(record.isGuest).isEqualTo(type == UserActionModel.ENTER_GUEST_MODE)
1085         assertThat(record.isAddUser).isEqualTo(type == UserActionModel.ADD_USER)
1086         assertThat(record.isAddSupervisedUser)
1087             .isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER)
1088     }
1089 
1090     private fun createUserInteractor(startAsProcessUser: Boolean = true) {
1091         val processUserId = Process.myUserHandle().identifier
1092         val startUserId = if (startAsProcessUser) processUserId else (processUserId + 1)
1093         runBlocking {
1094             val userInfo =
1095                 createUserInfo(id = startUserId, name = "user_$startUserId", isPrimary = true)
1096             userRepository.setUserInfos(listOf(userInfo))
1097             userRepository.setSelectedUserInfo(userInfo)
1098         }
1099         underTest =
1100             UserInteractor(
1101                 applicationContext = spyContext,
1102                 repository = userRepository,
1103                 activityStarter = activityStarter,
1104                 keyguardInteractor = keyguardReply.keyguardInteractor,
1105                 manager = manager,
1106                 headlessSystemUserMode = headlessSystemUserMode,
1107                 applicationScope = testScope.backgroundScope,
1108                 telephonyInteractor =
1109                     TelephonyInteractor(
1110                         repository = telephonyRepository,
1111                     ),
1112                 broadcastDispatcher = fakeBroadcastDispatcher,
1113                 keyguardUpdateMonitor = keyguardUpdateMonitor,
1114                 backgroundDispatcher = testDispatcher,
1115                 activityManager = activityManager,
1116                 refreshUsersScheduler = refreshUsersScheduler,
1117                 guestUserInteractor =
1118                     GuestUserInteractor(
1119                         applicationContext = spyContext,
1120                         applicationScope = testScope.backgroundScope,
1121                         mainDispatcher = testDispatcher,
1122                         backgroundDispatcher = testDispatcher,
1123                         manager = manager,
1124                         repository = userRepository,
1125                         deviceProvisionedController = deviceProvisionedController,
1126                         devicePolicyManager = devicePolicyManager,
1127                         refreshUsersScheduler = refreshUsersScheduler,
1128                         uiEventLogger = uiEventLogger,
1129                         resumeSessionReceiver = resumeSessionReceiver,
1130                         resetOrExitSessionReceiver = resetOrExitSessionReceiver,
1131                     ),
1132                 uiEventLogger = uiEventLogger,
1133                 featureFlags = featureFlags,
1134             )
1135     }
1136 
1137     private fun createUserInfos(
1138         count: Int,
1139         includeGuest: Boolean,
1140     ): List<UserInfo> {
1141         return (0 until count).map { index ->
1142             val isGuest = includeGuest && index == count - 1
1143             createUserInfo(
1144                 id = index,
1145                 name =
1146                     if (isGuest) {
1147                         "guest"
1148                     } else {
1149                         "user_$index"
1150                     },
1151                 isPrimary = !isGuest && index == 0,
1152                 isGuest = isGuest,
1153             )
1154         }
1155     }
1156 
1157     private fun createUserInfo(
1158         id: Int,
1159         name: String,
1160         isPrimary: Boolean = false,
1161         isGuest: Boolean = false,
1162     ): UserInfo {
1163         return UserInfo(
1164             id,
1165             name,
1166             /* iconPath= */ "",
1167             /* flags= */ if (isPrimary) {
1168                 UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL
1169             } else {
1170                 UserInfo.FLAG_FULL
1171             },
1172             if (isGuest) {
1173                 UserManager.USER_TYPE_FULL_GUEST
1174             } else {
1175                 UserManager.USER_TYPE_FULL_SYSTEM
1176             },
1177         )
1178     }
1179 
1180     companion object {
1181         private val ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
1182         private val GUEST_ICON: Drawable = mock()
1183         private const val SUPERVISED_USER_CREATION_APP_PACKAGE = "supervisedUserCreation"
1184     }
1185 }
1186