1 package com.android.systemui.biometrics.domain.interactor
2 
3 import android.hardware.biometrics.AuthenticateOptions
4 import android.hardware.biometrics.IBiometricContextListener
5 import android.hardware.biometrics.IBiometricContextListener.FoldState
6 import androidx.test.filters.SmallTest
7 import com.android.systemui.SysuiTestCase
8 import com.android.systemui.coroutines.collectLastValue
9 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
10 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
11 import com.android.systemui.keyguard.shared.model.KeyguardState
12 import com.android.systemui.keyguard.shared.model.TransitionState
13 import com.android.systemui.keyguard.shared.model.TransitionStep
14 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
15 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
16 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
17 import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
18 import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
19 import com.android.systemui.unfold.updates.FoldStateProvider
20 import com.android.systemui.util.mockito.withArgCaptor
21 import com.google.common.truth.Truth.assertThat
22 import kotlinx.coroutines.ExperimentalCoroutinesApi
23 import kotlinx.coroutines.test.TestScope
24 import kotlinx.coroutines.test.runCurrent
25 import kotlinx.coroutines.test.runTest
26 import org.junit.Before
27 import org.junit.Rule
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 import org.junit.runners.JUnit4
31 import org.mockito.Mock
32 import org.mockito.Mockito.verify
33 import org.mockito.junit.MockitoJUnit
34 
35 @OptIn(ExperimentalCoroutinesApi::class)
36 @SmallTest
37 @RunWith(JUnit4::class)
38 class LogContextInteractorImplTest : SysuiTestCase() {
39 
40     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
41 
42     private val testScope = TestScope()
43 
44     @Mock private lateinit var foldProvider: FoldStateProvider
45 
46     private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
47 
48     private lateinit var interactor: LogContextInteractorImpl
49 
50     @Before
51     fun setup() {
52         keyguardTransitionRepository = FakeKeyguardTransitionRepository()
53         interactor =
54             LogContextInteractorImpl(
55                 testScope.backgroundScope,
56                 foldProvider,
57                 KeyguardTransitionInteractorFactory.create(
58                         repository = keyguardTransitionRepository,
59                         scope = testScope.backgroundScope,
60                     )
61                     .keyguardTransitionInteractor,
62             )
63     }
64 
65     @Test
66     fun isAodChanges() =
67         testScope.runTest {
68             val isAod = collectLastValue(interactor.isAod)
69 
70             keyguardTransitionRepository.startTransitionTo(KeyguardState.OFF)
71             assertThat(isAod()).isFalse()
72 
73             keyguardTransitionRepository.startTransitionTo(KeyguardState.DOZING)
74             assertThat(isAod()).isFalse()
75 
76             keyguardTransitionRepository.startTransitionTo(KeyguardState.DREAMING)
77             assertThat(isAod()).isFalse()
78 
79             keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
80             assertThat(isAod()).isTrue()
81 
82             keyguardTransitionRepository.startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
83             assertThat(isAod()).isFalse()
84 
85             keyguardTransitionRepository.startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
86             assertThat(isAod()).isFalse()
87 
88             keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
89             assertThat(isAod()).isFalse()
90 
91             keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE)
92             assertThat(isAod()).isFalse()
93 
94             keyguardTransitionRepository.startTransitionTo(KeyguardState.OCCLUDED)
95             assertThat(isAod()).isFalse()
96         }
97 
98     @Test
99     fun isAwakeChanges() =
100         testScope.runTest {
101             val isAwake = collectLastValue(interactor.isAwake)
102 
103             keyguardTransitionRepository.startTransitionTo(KeyguardState.OFF)
104             assertThat(isAwake()).isFalse()
105 
106             keyguardTransitionRepository.startTransitionTo(KeyguardState.DOZING)
107             assertThat(isAwake()).isFalse()
108 
109             keyguardTransitionRepository.startTransitionTo(KeyguardState.DREAMING)
110             assertThat(isAwake()).isTrue()
111 
112             keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
113             assertThat(isAwake()).isFalse()
114 
115             keyguardTransitionRepository.startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
116             assertThat(isAwake()).isTrue()
117 
118             keyguardTransitionRepository.startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
119             assertThat(isAwake()).isTrue()
120 
121             keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
122             assertThat(isAwake()).isTrue()
123 
124             keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE)
125             assertThat(isAwake()).isTrue()
126 
127             keyguardTransitionRepository.startTransitionTo(KeyguardState.OCCLUDED)
128             assertThat(isAwake()).isTrue()
129         }
130 
131     @Test
132     fun displayStateChanges() =
133         testScope.runTest {
134             val displayState = collectLastValue(interactor.displayState)
135 
136             keyguardTransitionRepository.startTransitionTo(KeyguardState.OFF)
137             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_NO_UI)
138 
139             keyguardTransitionRepository.startTransitionTo(KeyguardState.DOZING)
140             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_NO_UI)
141 
142             keyguardTransitionRepository.startTransitionTo(KeyguardState.DREAMING)
143             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_SCREENSAVER)
144 
145             keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
146             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
147 
148             keyguardTransitionRepository.startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
149             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
150 
151             keyguardTransitionRepository.startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
152             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
153 
154             keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
155             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
156 
157             keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE)
158             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_UNKNOWN)
159 
160             keyguardTransitionRepository.startTransitionTo(KeyguardState.OCCLUDED)
161             assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
162         }
163 
164     @Test
165     fun foldStateChanges() =
166         testScope.runTest {
167             val foldState = collectLastValue(interactor.foldState)
168             runCurrent()
169             val listener = foldProvider.captureListener()
170 
171             listener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
172             assertThat(foldState()).isEqualTo(FoldState.UNKNOWN)
173 
174             listener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
175             assertThat(foldState()).isEqualTo(FoldState.HALF_OPENED)
176 
177             listener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
178             assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
179 
180             listener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
181             assertThat(foldState()).isEqualTo(FoldState.FULLY_OPENED)
182 
183             listener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
184             assertThat(foldState()).isEqualTo(FoldState.FULLY_CLOSED)
185         }
186 
187     @Test
188     fun contextSubscriberChanges() =
189         testScope.runTest {
190             runCurrent()
191             val foldListener = foldProvider.captureListener()
192             foldListener.onFoldUpdate(FOLD_UPDATE_START_CLOSING)
193             foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
194             keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
195 
196             var folded: Int? = null
197             var displayState: Int? = null
198             val job =
199                 interactor.addBiometricContextListener(
200                     object : IBiometricContextListener.Stub() {
201                         override fun onFoldChanged(foldState: Int) {
202                             folded = foldState
203                         }
204 
205                         override fun onDisplayStateChanged(newDisplayState: Int) {
206                             displayState = newDisplayState
207                         }
208                     }
209                 )
210             runCurrent()
211 
212             assertThat(folded).isEqualTo(FoldState.FULLY_CLOSED)
213             assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_AOD)
214 
215             foldListener.onFoldUpdate(FOLD_UPDATE_START_OPENING)
216             foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
217             keyguardTransitionRepository.startTransitionTo(KeyguardState.LOCKSCREEN)
218             runCurrent()
219 
220             assertThat(folded).isEqualTo(FoldState.HALF_OPENED)
221             assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
222 
223             job.cancel()
224 
225             // stale updates should be ignored
226             foldListener.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
227             keyguardTransitionRepository.startTransitionTo(KeyguardState.AOD)
228             runCurrent()
229 
230             assertThat(folded).isEqualTo(FoldState.HALF_OPENED)
231             assertThat(displayState).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN)
232         }
233 }
234 
235 private suspend fun FakeKeyguardTransitionRepository.startTransitionTo(newState: KeyguardState) =
236     sendTransitionStep(TransitionStep(to = newState, transitionState = TransitionState.STARTED))
237 
238 private fun FoldStateProvider.captureListener() =
239     withArgCaptor<FoldStateProvider.FoldUpdatesListener> {
240         verify(this@captureListener).addCallback(capture())
241     }
242