• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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 package com.android.systemui.keyguard.data.repository
18 
19 import android.graphics.Point
20 import android.hardware.biometrics.BiometricSourceType
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import com.android.keyguard.KeyguardUpdateMonitor
24 import com.android.keyguard.KeyguardUpdateMonitorCallback
25 import com.android.systemui.RoboPilotTest
26 import com.android.systemui.SysuiTestCase
27 import com.android.systemui.biometrics.AuthController
28 import com.android.systemui.common.shared.model.Position
29 import com.android.systemui.coroutines.collectLastValue
30 import com.android.systemui.doze.DozeMachine
31 import com.android.systemui.doze.DozeTransitionCallback
32 import com.android.systemui.doze.DozeTransitionListener
33 import com.android.systemui.dreams.DreamOverlayCallbackController
34 import com.android.systemui.keyguard.ScreenLifecycle
35 import com.android.systemui.keyguard.WakefulnessLifecycle
36 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
37 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
38 import com.android.systemui.keyguard.shared.model.DozeStateModel
39 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
40 import com.android.systemui.keyguard.shared.model.ScreenModel
41 import com.android.systemui.keyguard.shared.model.ScreenState
42 import com.android.systemui.keyguard.shared.model.WakefulnessModel
43 import com.android.systemui.keyguard.shared.model.WakefulnessState
44 import com.android.systemui.plugins.statusbar.StatusBarStateController
45 import com.android.systemui.statusbar.phone.BiometricUnlockController
46 import com.android.systemui.statusbar.phone.DozeParameters
47 import com.android.systemui.statusbar.phone.KeyguardBypassController
48 import com.android.systemui.statusbar.policy.KeyguardStateController
49 import com.android.systemui.util.mockito.argumentCaptor
50 import com.android.systemui.util.mockito.whenever
51 import com.android.systemui.util.mockito.withArgCaptor
52 import com.android.systemui.util.time.FakeSystemClock
53 import com.google.common.truth.Truth.assertThat
54 import kotlinx.coroutines.ExperimentalCoroutinesApi
55 import kotlinx.coroutines.flow.launchIn
56 import kotlinx.coroutines.flow.onCompletion
57 import kotlinx.coroutines.flow.onEach
58 import kotlinx.coroutines.test.StandardTestDispatcher
59 import kotlinx.coroutines.test.TestScope
60 import kotlinx.coroutines.test.runCurrent
61 import kotlinx.coroutines.test.runTest
62 import org.junit.Before
63 import org.junit.Test
64 import org.junit.runner.RunWith
65 import org.mockito.Mock
66 import org.mockito.Mockito.atLeastOnce
67 import org.mockito.Mockito.verify
68 import org.mockito.MockitoAnnotations
69 
70 @OptIn(ExperimentalCoroutinesApi::class)
71 @SmallTest
72 @RoboPilotTest
73 @RunWith(AndroidJUnit4::class)
74 class KeyguardRepositoryImplTest : SysuiTestCase() {
75 
76     @Mock private lateinit var statusBarStateController: StatusBarStateController
77     @Mock private lateinit var keyguardStateController: KeyguardStateController
78     @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
79     @Mock private lateinit var screenLifecycle: ScreenLifecycle
80     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
81     @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
82     @Mock private lateinit var authController: AuthController
83     @Mock private lateinit var keyguardBypassController: KeyguardBypassController
84     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
85     @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
86     @Mock private lateinit var dozeParameters: DozeParameters
87     private val mainDispatcher = StandardTestDispatcher()
88     private val testDispatcher = StandardTestDispatcher()
89     private val testScope = TestScope(testDispatcher)
90     private lateinit var systemClock: FakeSystemClock
91 
92     private lateinit var underTest: KeyguardRepositoryImpl
93 
94     @Before
95     fun setUp() {
96         MockitoAnnotations.initMocks(this)
97         systemClock = FakeSystemClock()
98         underTest =
99             KeyguardRepositoryImpl(
100                 statusBarStateController,
101                 wakefulnessLifecycle,
102                 screenLifecycle,
103                 biometricUnlockController,
104                 keyguardStateController,
105                 keyguardBypassController,
106                 keyguardUpdateMonitor,
107                 dozeTransitionListener,
108                 dozeParameters,
109                 authController,
110                 dreamOverlayCallbackController,
111                 mainDispatcher,
112                 testScope.backgroundScope,
113                 systemClock,
114             )
115     }
116 
117     @Test
118     fun animateBottomAreaDozingTransitions() =
119         testScope.runTest {
120             assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
121 
122             underTest.setAnimateDozingTransitions(true)
123             assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
124 
125             underTest.setAnimateDozingTransitions(false)
126             assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
127 
128             underTest.setAnimateDozingTransitions(true)
129             assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
130         }
131 
132     @Test
133     fun bottomAreaAlpha() =
134         testScope.runTest {
135             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
136 
137             underTest.setBottomAreaAlpha(0.1f)
138             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
139 
140             underTest.setBottomAreaAlpha(0.2f)
141             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
142 
143             underTest.setBottomAreaAlpha(0.3f)
144             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
145 
146             underTest.setBottomAreaAlpha(0.5f)
147             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
148 
149             underTest.setBottomAreaAlpha(1.0f)
150             assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
151         }
152 
153     @Test
154     fun clockPosition() =
155         testScope.runTest {
156             assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
157 
158             underTest.setClockPosition(0, 1)
159             assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
160 
161             underTest.setClockPosition(1, 9)
162             assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
163 
164             underTest.setClockPosition(1, 0)
165             assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
166 
167             underTest.setClockPosition(3, 1)
168             assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
169         }
170 
171     @Test
172     fun dozeTimeTick() =
173         testScope.runTest {
174             val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
175             assertThat(lastDozeTimeTick).isEqualTo(0L)
176 
177             // WHEN dozeTimeTick updated
178             systemClock.setUptimeMillis(systemClock.uptimeMillis() + 5)
179             underTest.dozeTimeTick()
180 
181             // THEN listeners were updated to the latest uptime millis
182             assertThat(systemClock.uptimeMillis()).isEqualTo(lastDozeTimeTick)
183         }
184 
185     @Test
186     fun isKeyguardShowing() =
187         testScope.runTest {
188             whenever(keyguardStateController.isShowing).thenReturn(false)
189             var latest: Boolean? = null
190             val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
191 
192             runCurrent()
193             assertThat(latest).isFalse()
194             assertThat(underTest.isKeyguardShowing()).isFalse()
195 
196             val captor = argumentCaptor<KeyguardStateController.Callback>()
197             verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
198 
199             whenever(keyguardStateController.isShowing).thenReturn(true)
200             captor.value.onKeyguardShowingChanged()
201             runCurrent()
202             assertThat(latest).isTrue()
203             assertThat(underTest.isKeyguardShowing()).isTrue()
204 
205             whenever(keyguardStateController.isShowing).thenReturn(false)
206             captor.value.onKeyguardShowingChanged()
207             runCurrent()
208             assertThat(latest).isFalse()
209             assertThat(underTest.isKeyguardShowing()).isFalse()
210 
211             job.cancel()
212         }
213 
214     @Test
215     fun isBypassEnabled_disabledInController() {
216         whenever(keyguardBypassController.isBypassEnabled).thenReturn(false)
217         whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
218         assertThat(underTest.isBypassEnabled()).isFalse()
219     }
220 
221     @Test
222     fun isBypassEnabled_enabledInController() {
223         whenever(keyguardBypassController.isBypassEnabled).thenReturn(true)
224         whenever(keyguardBypassController.bypassEnabled).thenReturn(true)
225         assertThat(underTest.isBypassEnabled()).isTrue()
226     }
227 
228     @Test
229     fun isAodAvailable() = runTest {
230         val flow = underTest.isAodAvailable
231         var isAodAvailable = collectLastValue(flow)
232         runCurrent()
233 
234         val callback =
235             withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
236 
237         whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
238         callback.onAlwaysOnChange()
239         assertThat(isAodAvailable()).isEqualTo(false)
240 
241         whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
242         callback.onAlwaysOnChange()
243         assertThat(isAodAvailable()).isEqualTo(true)
244 
245         flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
246     }
247 
248     @Test
249     fun isKeyguardOccluded() =
250         testScope.runTest {
251             whenever(keyguardStateController.isOccluded).thenReturn(false)
252             var latest: Boolean? = null
253             val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this)
254 
255             runCurrent()
256             assertThat(latest).isFalse()
257 
258             val captor = argumentCaptor<KeyguardStateController.Callback>()
259             verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
260 
261             whenever(keyguardStateController.isOccluded).thenReturn(true)
262             captor.value.onKeyguardShowingChanged()
263             runCurrent()
264             assertThat(latest).isTrue()
265 
266             whenever(keyguardStateController.isOccluded).thenReturn(false)
267             captor.value.onKeyguardShowingChanged()
268             runCurrent()
269             assertThat(latest).isFalse()
270 
271             job.cancel()
272         }
273 
274     @Test
275     fun isKeyguardUnlocked() =
276         testScope.runTest {
277             whenever(keyguardStateController.isUnlocked).thenReturn(false)
278             val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked)
279 
280             runCurrent()
281             assertThat(isKeyguardUnlocked).isFalse()
282 
283             val captor = argumentCaptor<KeyguardStateController.Callback>()
284             verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
285 
286             whenever(keyguardStateController.isUnlocked).thenReturn(true)
287             captor.value.onUnlockedChanged()
288             runCurrent()
289             assertThat(isKeyguardUnlocked).isTrue()
290 
291             whenever(keyguardStateController.isUnlocked).thenReturn(false)
292             captor.value.onKeyguardShowingChanged()
293             runCurrent()
294             assertThat(isKeyguardUnlocked).isFalse()
295         }
296 
297     @Test
298     fun isDozing() =
299         testScope.runTest {
300             underTest.setIsDozing(true)
301             assertThat(underTest.isDozing.value).isEqualTo(true)
302 
303             underTest.setIsDozing(false)
304             assertThat(underTest.isDozing.value).isEqualTo(false)
305         }
306 
307     @Test
308     fun isDozing_startsWithCorrectInitialValueForIsDozing() =
309         testScope.runTest {
310             assertThat(underTest.lastDozeTapToWakePosition.value).isEqualTo(null)
311 
312             val expectedPoint = Point(100, 200)
313             underTest.setLastDozeTapToWakePosition(expectedPoint)
314             assertThat(underTest.lastDozeTapToWakePosition.value).isEqualTo(expectedPoint)
315         }
316 
317     @Test
318     fun dozeAmount() =
319         testScope.runTest {
320             val values = mutableListOf<Float>()
321             val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
322 
323             val captor = argumentCaptor<StatusBarStateController.StateListener>()
324             runCurrent()
325             verify(statusBarStateController).addCallback(captor.capture())
326 
327             captor.value.onDozeAmountChanged(0.433f, 0.4f)
328             runCurrent()
329             captor.value.onDozeAmountChanged(0.498f, 0.5f)
330             runCurrent()
331             captor.value.onDozeAmountChanged(0.661f, 0.65f)
332             runCurrent()
333 
334             assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
335 
336             job.cancel()
337             runCurrent()
338             verify(statusBarStateController).removeCallback(captor.value)
339         }
340 
341     @Test
342     fun isActiveDreamLockscreenHosted() =
343         testScope.runTest {
344             underTest.setIsActiveDreamLockscreenHosted(true)
345             assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(true)
346 
347             underTest.setIsActiveDreamLockscreenHosted(false)
348             assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(false)
349         }
350 
351     @Test
352     fun wakefulness() =
353         testScope.runTest {
354             val values = mutableListOf<WakefulnessModel>()
355             val job = underTest.wakefulness.onEach(values::add).launchIn(this)
356 
357             runCurrent()
358             val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
359             verify(wakefulnessLifecycle).addObserver(captor.capture())
360 
361             whenever(wakefulnessLifecycle.wakefulness)
362                 .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
363             captor.value.onStartedWakingUp()
364             runCurrent()
365 
366             whenever(wakefulnessLifecycle.wakefulness)
367                 .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
368             captor.value.onFinishedWakingUp()
369             runCurrent()
370 
371             whenever(wakefulnessLifecycle.wakefulness)
372                 .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
373             captor.value.onStartedGoingToSleep()
374             runCurrent()
375 
376             whenever(wakefulnessLifecycle.wakefulness)
377                 .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
378             captor.value.onFinishedGoingToSleep()
379             runCurrent()
380 
381             assertThat(values.map { it.state })
382                 .isEqualTo(
383                     listOf(
384                         // Initial value will be ASLEEP
385                         WakefulnessState.ASLEEP,
386                         WakefulnessState.STARTING_TO_WAKE,
387                         WakefulnessState.AWAKE,
388                         WakefulnessState.STARTING_TO_SLEEP,
389                         WakefulnessState.ASLEEP,
390                     )
391                 )
392 
393             job.cancel()
394         }
395 
396     @Test
397     fun screenModel() =
398         testScope.runTest {
399             val values = mutableListOf<ScreenModel>()
400             val job = underTest.screenModel.onEach(values::add).launchIn(this)
401 
402             runCurrent()
403             val captor = argumentCaptor<ScreenLifecycle.Observer>()
404             verify(screenLifecycle).addObserver(captor.capture())
405 
406             whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_TURNING_ON)
407             captor.value.onScreenTurningOn()
408             runCurrent()
409 
410             whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON)
411             captor.value.onScreenTurnedOn()
412             runCurrent()
413 
414             whenever(screenLifecycle.getScreenState())
415                 .thenReturn(ScreenLifecycle.SCREEN_TURNING_OFF)
416             captor.value.onScreenTurningOff()
417             runCurrent()
418 
419             whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_OFF)
420             captor.value.onScreenTurnedOff()
421             runCurrent()
422 
423             assertThat(values.map { it.state })
424                 .isEqualTo(
425                     listOf(
426                         // Initial value will be OFF
427                         ScreenState.SCREEN_OFF,
428                         ScreenState.SCREEN_TURNING_ON,
429                         ScreenState.SCREEN_ON,
430                         ScreenState.SCREEN_TURNING_OFF,
431                         ScreenState.SCREEN_OFF,
432                     )
433                 )
434 
435             job.cancel()
436         }
437 
438     @Test
439     fun isUdfpsSupported() =
440         testScope.runTest {
441             whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
442             assertThat(underTest.isUdfpsSupported()).isTrue()
443 
444             whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
445             assertThat(underTest.isUdfpsSupported()).isFalse()
446         }
447 
448     @Test
449     fun isKeyguardGoingAway() =
450         testScope.runTest {
451             whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
452             var latest: Boolean? = null
453             val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
454             runCurrent()
455             assertThat(latest).isFalse()
456 
457             val captor = argumentCaptor<KeyguardStateController.Callback>()
458             verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
459 
460             whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
461             captor.value.onKeyguardGoingAwayChanged()
462             runCurrent()
463             assertThat(latest).isTrue()
464 
465             whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
466             captor.value.onKeyguardGoingAwayChanged()
467             runCurrent()
468             assertThat(latest).isFalse()
469 
470             job.cancel()
471         }
472 
473     @Test
474     fun isDreamingFromKeyguardUpdateMonitor() =
475         TestScope(mainDispatcher).runTest {
476             whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
477             var latest: Boolean? = null
478             val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
479 
480             runCurrent()
481             assertThat(latest).isFalse()
482 
483             val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
484             verify(keyguardUpdateMonitor).registerCallback(captor.capture())
485 
486             captor.value.onDreamingStateChanged(true)
487             runCurrent()
488             assertThat(latest).isTrue()
489 
490             captor.value.onDreamingStateChanged(false)
491             runCurrent()
492             assertThat(latest).isFalse()
493 
494             job.cancel()
495         }
496 
497     @Test
498     fun isDreamingFromDreamOverlayCallbackController() =
499         testScope.runTest {
500             whenever(dreamOverlayCallbackController.isDreaming).thenReturn(false)
501             var latest: Boolean? = null
502             val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this)
503 
504             runCurrent()
505             assertThat(latest).isFalse()
506 
507             val listener =
508                 withArgCaptor<DreamOverlayCallbackController.Callback> {
509                     verify(dreamOverlayCallbackController).addCallback(capture())
510                 }
511 
512             listener.onStartDream()
513             runCurrent()
514             assertThat(latest).isTrue()
515 
516             listener.onWakeUp()
517             runCurrent()
518             assertThat(latest).isFalse()
519 
520             job.cancel()
521         }
522 
523     @Test
524     fun biometricUnlockState() =
525         testScope.runTest {
526             val values = mutableListOf<BiometricUnlockModel>()
527             val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
528 
529             runCurrent()
530             val captor = argumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>()
531             verify(biometricUnlockController).addListener(captor.capture())
532 
533             listOf(
534                     BiometricUnlockController.MODE_NONE,
535                     BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
536                     BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
537                     BiometricUnlockController.MODE_SHOW_BOUNCER,
538                     BiometricUnlockController.MODE_ONLY_WAKE,
539                     BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
540                     BiometricUnlockController.MODE_DISMISS_BOUNCER,
541                     BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
542                 )
543                 .forEach {
544                     whenever(biometricUnlockController.mode).thenReturn(it)
545                     captor.value.onModeChanged(it)
546                     runCurrent()
547                 }
548 
549             assertThat(values)
550                 .isEqualTo(
551                     listOf(
552                         // Initial value will be NONE, followed by onModeChanged() call
553                         BiometricUnlockModel.NONE,
554                         BiometricUnlockModel.NONE,
555                         BiometricUnlockModel.WAKE_AND_UNLOCK,
556                         BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
557                         BiometricUnlockModel.SHOW_BOUNCER,
558                         BiometricUnlockModel.ONLY_WAKE,
559                         BiometricUnlockModel.UNLOCK_COLLAPSING,
560                         BiometricUnlockModel.DISMISS_BOUNCER,
561                         BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
562                     )
563                 )
564 
565             job.cancel()
566             runCurrent()
567             verify(biometricUnlockController).removeListener(captor.value)
568         }
569 
570     @Test
571     fun dozeTransitionModel() =
572         testScope.runTest {
573             // For the initial state
574             whenever(dozeTransitionListener.oldState).thenReturn(DozeMachine.State.UNINITIALIZED)
575             whenever(dozeTransitionListener.newState).thenReturn(DozeMachine.State.UNINITIALIZED)
576 
577             val values = mutableListOf<DozeTransitionModel>()
578             val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)
579 
580             runCurrent()
581             val listener =
582                 withArgCaptor<DozeTransitionCallback> {
583                     verify(dozeTransitionListener).addCallback(capture())
584                 }
585 
586             // These don't have to reflect real transitions from the DozeMachine. Only that the
587             // transitions are properly emitted
588             listener.onDozeTransition(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE)
589             runCurrent()
590             listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
591             runCurrent()
592             listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
593             runCurrent()
594             listener.onDozeTransition(
595                 DozeMachine.State.DOZE_REQUEST_PULSE,
596                 DozeMachine.State.DOZE_PULSING
597             )
598             runCurrent()
599             listener.onDozeTransition(
600                 DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
601                 DozeMachine.State.DOZE_PULSE_DONE
602             )
603             runCurrent()
604             listener.onDozeTransition(
605                 DozeMachine.State.DOZE_AOD_PAUSING,
606                 DozeMachine.State.DOZE_AOD_PAUSED
607             )
608             runCurrent()
609 
610             assertThat(values)
611                 .isEqualTo(
612                     listOf(
613                         // Initial value will be UNINITIALIZED
614                         DozeTransitionModel(
615                             DozeStateModel.UNINITIALIZED,
616                             DozeStateModel.UNINITIALIZED
617                         ),
618                         DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE),
619                         DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD),
620                         DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH),
621                         DozeTransitionModel(
622                             DozeStateModel.DOZE_REQUEST_PULSE,
623                             DozeStateModel.DOZE_PULSING
624                         ),
625                         DozeTransitionModel(
626                             DozeStateModel.DOZE_SUSPEND_TRIGGERS,
627                             DozeStateModel.DOZE_PULSE_DONE
628                         ),
629                         DozeTransitionModel(
630                             DozeStateModel.DOZE_AOD_PAUSING,
631                             DozeStateModel.DOZE_AOD_PAUSED
632                         ),
633                     )
634                 )
635 
636             job.cancel()
637             runCurrent()
638             verify(dozeTransitionListener).removeCallback(listener)
639         }
640 
641     @Test
642     fun fingerprintSensorLocation() =
643         testScope.runTest {
644             val values = mutableListOf<Point?>()
645             val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
646 
647             runCurrent()
648             val captor = argumentCaptor<AuthController.Callback>()
649             verify(authController).addCallback(captor.capture())
650 
651             // An initial, null value should be initially emitted so that flows combined with this
652             // one
653             // emit values immediately. The sensor location is expected to be nullable, so anyone
654             // consuming it should handle that properly.
655             assertThat(values).isEqualTo(listOf(null))
656 
657             listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
658                 .onEach {
659                     whenever(authController.fingerprintSensorLocation).thenReturn(it)
660                     captor.value.onFingerprintLocationChanged()
661                     runCurrent()
662                 }
663                 .also { dispatchedSensorLocations ->
664                     assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
665                 }
666 
667             job.cancel()
668         }
669 
670     @Test
671     fun faceSensorLocation() =
672         testScope.runTest {
673             val values = mutableListOf<Point?>()
674             val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
675 
676             val captor = argumentCaptor<AuthController.Callback>()
677             runCurrent()
678             verify(authController).addCallback(captor.capture())
679 
680             // An initial, null value should be initially emitted so that flows combined with this
681             // one
682             // emit values immediately. The sensor location is expected to be nullable, so anyone
683             // consuming it should handle that properly.
684             assertThat(values).isEqualTo(listOf(null))
685 
686             listOf(
687                     Point(500, 500),
688                     Point(0, 0),
689                     null,
690                     Point(250, 250),
691                 )
692                 .onEach {
693                     whenever(authController.faceSensorLocation).thenReturn(it)
694                     captor.value.onFaceSensorLocationChanged()
695                     runCurrent()
696                 }
697                 .also { dispatchedSensorLocations ->
698                     assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
699                 }
700 
701             job.cancel()
702         }
703 
704     @Test
705     fun biometricUnlockSource() =
706         testScope.runTest {
707             val values = mutableListOf<BiometricUnlockSource?>()
708             val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
709 
710             runCurrent()
711             val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
712             verify(keyguardUpdateMonitor).registerCallback(captor.capture())
713 
714             // An initial, null value should be initially emitted so that flows combined with this
715             // one
716             // emit values immediately. The biometric unlock source is expected to be nullable, so
717             // anyone consuming it should handle that properly.
718             assertThat(values).isEqualTo(listOf(null))
719 
720             listOf(
721                     BiometricSourceType.FINGERPRINT,
722                     BiometricSourceType.IRIS,
723                     null,
724                     BiometricSourceType.FACE,
725                     BiometricSourceType.FINGERPRINT,
726                 )
727                 .onEach { biometricSourceType ->
728                     captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
729                     runCurrent()
730                 }
731 
732             assertThat(values)
733                 .isEqualTo(
734                     listOf(
735                         null,
736                         BiometricUnlockSource.FINGERPRINT_SENSOR,
737                         BiometricUnlockSource.FACE_SENSOR,
738                         null,
739                         BiometricUnlockSource.FACE_SENSOR,
740                         BiometricUnlockSource.FINGERPRINT_SENSOR,
741                     )
742                 )
743 
744             job.cancel()
745         }
746 }
747