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.keyguard.data.repository
19 
20 import android.app.admin.DevicePolicyManager
21 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE
22 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
23 import android.content.Intent
24 import android.content.pm.UserInfo
25 import android.hardware.biometrics.BiometricManager
26 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
27 import android.testing.TestableLooper
28 import androidx.test.ext.junit.runners.AndroidJUnit4
29 import androidx.test.filters.SmallTest
30 import com.android.internal.widget.LockPatternUtils
31 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
32 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
33 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
34 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
35 import com.android.systemui.R
36 import com.android.systemui.RoboPilotTest
37 import com.android.systemui.SysuiTestCase
38 import com.android.systemui.biometrics.AuthController
39 import com.android.systemui.biometrics.data.repository.FaceSensorInfo
40 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
41 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
42 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
43 import com.android.systemui.biometrics.shared.model.SensorStrength
44 import com.android.systemui.coroutines.collectLastValue
45 import com.android.systemui.dump.DumpManager
46 import com.android.systemui.keyguard.data.repository.BiometricType.FACE
47 import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPRINT
48 import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
49 import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
50 import com.android.systemui.keyguard.shared.model.DevicePosture
51 import com.android.systemui.statusbar.policy.DevicePostureController
52 import com.android.systemui.user.data.repository.FakeUserRepository
53 import com.android.systemui.util.mockito.eq
54 import com.android.systemui.util.mockito.whenever
55 import com.google.common.truth.Truth.assertThat
56 import kotlinx.coroutines.ExperimentalCoroutinesApi
57 import kotlinx.coroutines.test.StandardTestDispatcher
58 import kotlinx.coroutines.test.TestDispatcher
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.ArgumentCaptor
66 import org.mockito.ArgumentMatchers.any
67 import org.mockito.ArgumentMatchers.anyInt
68 import org.mockito.ArgumentMatchers.isNull
69 import org.mockito.Captor
70 import org.mockito.Mock
71 import org.mockito.Mockito.atLeastOnce
72 import org.mockito.Mockito.clearInvocations
73 import org.mockito.Mockito.times
74 import org.mockito.Mockito.verify
75 import org.mockito.MockitoAnnotations
76 
77 @OptIn(ExperimentalCoroutinesApi::class)
78 @SmallTest
79 @RoboPilotTest
80 @TestableLooper.RunWithLooper(setAsMainLooper = true)
81 @RunWith(AndroidJUnit4::class)
82 class BiometricSettingsRepositoryTest : SysuiTestCase() {
83     private lateinit var underTest: BiometricSettingsRepository
84 
85     @Mock private lateinit var authController: AuthController
86     @Mock private lateinit var lockPatternUtils: LockPatternUtils
87     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
88     @Mock private lateinit var dumpManager: DumpManager
89     @Mock private lateinit var biometricManager: BiometricManager
90     @Captor
91     private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
92     @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
93     @Captor
94     private lateinit var biometricManagerCallback:
95         ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub>
96     private lateinit var userRepository: FakeUserRepository
97     private lateinit var devicePostureRepository: FakeDevicePostureRepository
98     private lateinit var facePropertyRepository: FakeFacePropertyRepository
99     private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
100 
101     private lateinit var testDispatcher: TestDispatcher
102     private lateinit var testScope: TestScope
103     private var testableLooper: TestableLooper? = null
104 
105     @Before
106     fun setUp() {
107         MockitoAnnotations.initMocks(this)
108         testableLooper = TestableLooper.get(this)
109         testDispatcher = StandardTestDispatcher()
110         testScope = TestScope(testDispatcher)
111         userRepository = FakeUserRepository()
112         devicePostureRepository = FakeDevicePostureRepository()
113         facePropertyRepository = FakeFacePropertyRepository()
114         fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
115     }
116 
117     private suspend fun createBiometricSettingsRepository() {
118         userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
119         userRepository.setSelectedUserInfo(PRIMARY_USER)
120         underTest =
121             BiometricSettingsRepositoryImpl(
122                 context = context,
123                 lockPatternUtils = lockPatternUtils,
124                 broadcastDispatcher = fakeBroadcastDispatcher,
125                 authController = authController,
126                 userRepository = userRepository,
127                 devicePolicyManager = devicePolicyManager,
128                 scope = testScope.backgroundScope,
129                 backgroundDispatcher = testDispatcher,
130                 biometricManager = biometricManager,
131                 devicePostureRepository = devicePostureRepository,
132                 dumpManager = dumpManager,
133                 facePropertyRepository = facePropertyRepository,
134                 fingerprintPropertyRepository = fingerprintPropertyRepository,
135             )
136         testScope.runCurrent()
137         fingerprintPropertyRepository.setProperties(
138             1,
139             SensorStrength.STRONG,
140             FingerprintSensorType.UDFPS_OPTICAL,
141             emptyMap()
142         )
143         verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
144         verify(authController, atLeastOnce()).addCallback(authControllerCallback.capture())
145     }
146 
147     @Test
148     fun fingerprintEnrollmentChange() =
149         testScope.runTest {
150             createBiometricSettingsRepository()
151             val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
152             runCurrent()
153 
154             whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true)
155             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
156             assertThat(fingerprintAllowed()).isTrue()
157 
158             whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false)
159             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, ANOTHER_USER_ID, false)
160             assertThat(fingerprintAllowed()).isTrue()
161 
162             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, false)
163             assertThat(fingerprintAllowed()).isFalse()
164         }
165 
166     @Test
167     fun strongBiometricAllowedChange() =
168         testScope.runTest {
169             fingerprintIsEnrolled()
170             doNotDisableKeyguardAuthFeatures()
171             createBiometricSettingsRepository()
172 
173             val strongBiometricAllowed by
174                 collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
175             runCurrent()
176 
177             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
178             assertThat(strongBiometricAllowed).isTrue()
179 
180             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID)
181             assertThat(strongBiometricAllowed).isFalse()
182         }
183 
184     @Test
185     fun convenienceBiometricAllowedChange() =
186         testScope.runTest {
187             overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
188             deviceIsInPostureThatSupportsFaceAuth()
189             faceAuthIsEnrolled()
190             faceAuthIsNonStrongBiometric()
191             createBiometricSettingsRepository()
192             val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
193             doNotDisableKeyguardAuthFeatures()
194             faceAuthIsEnabledByBiometricManager()
195 
196             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
197             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
198             runCurrent()
199             assertThat(convenienceFaceAuthAllowed).isTrue()
200 
201             onNonStrongAuthChanged(false, ANOTHER_USER_ID)
202             assertThat(convenienceFaceAuthAllowed).isTrue()
203 
204             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
205             assertThat(convenienceFaceAuthAllowed).isFalse()
206             mContext.orCreateTestableResources.removeOverride(
207                 com.android.internal.R.bool.config_strongAuthRequiredOnBoot
208             )
209         }
210 
211     private fun faceAuthIsNonStrongBiometric() {
212         facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE))
213     }
214 
215     private fun faceAuthIsStrongBiometric() {
216         facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
217     }
218 
219     private fun deviceIsInPostureThatSupportsFaceAuth() {
220         overrideResource(
221             R.integer.config_face_auth_supported_posture,
222             DevicePostureController.DEVICE_POSTURE_FLIPPED
223         )
224         devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED)
225     }
226 
227     @Test
228     fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() =
229         testScope.runTest {
230             overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true)
231             createBiometricSettingsRepository()
232             faceAuthIsNonStrongBiometric()
233             faceAuthIsEnrolled()
234             doNotDisableKeyguardAuthFeatures()
235 
236             val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
237             runCurrent()
238             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
239 
240             assertThat(convenienceBiometricAllowed()).isFalse()
241             mContext.orCreateTestableResources.removeOverride(
242                 com.android.internal.R.bool.config_strongAuthRequiredOnBoot
243             )
244         }
245 
246     @Test
247     fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() =
248         testScope.runTest {
249             overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
250             faceAuthIsNonStrongBiometric()
251             deviceIsInPostureThatSupportsFaceAuth()
252             faceAuthIsEnrolled()
253             createBiometricSettingsRepository()
254             doNotDisableKeyguardAuthFeatures()
255             faceAuthIsEnabledByBiometricManager()
256             runCurrent()
257 
258             val convenienceBiometricAllowed by
259                 collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
260 
261             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
262             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
263             runCurrent()
264             assertThat(convenienceBiometricAllowed).isTrue()
265 
266             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID)
267             assertThat(convenienceBiometricAllowed).isFalse()
268             mContext.orCreateTestableResources.removeOverride(
269                 com.android.internal.R.bool.config_strongAuthRequiredOnBoot
270             )
271         }
272 
273     private fun onStrongAuthChanged(flags: Int, userId: Int) {
274         strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
275         testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
276     }
277 
278     private fun onNonStrongAuthChanged(allowed: Boolean, userId: Int) {
279         strongAuthTracker.value.stub.onIsNonStrongBiometricAllowedChanged(allowed, userId)
280         testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
281     }
282 
283     @Test
284     fun fingerprintDisabledByDpmChange() =
285         testScope.runTest {
286             fingerprintIsEnrolled(PRIMARY_USER_ID)
287             createBiometricSettingsRepository()
288 
289             val fingerprintEnabledByDevicePolicy =
290                 collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
291             runCurrent()
292 
293             whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
294                 .thenReturn(KEYGUARD_DISABLE_FINGERPRINT)
295             broadcastDPMStateChange()
296             assertThat(fingerprintEnabledByDevicePolicy()).isFalse()
297 
298             whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())).thenReturn(0)
299             broadcastDPMStateChange()
300             assertThat(fingerprintEnabledByDevicePolicy()).isTrue()
301         }
302 
303     private fun fingerprintIsEnrolled(userId: Int = PRIMARY_USER_ID) {
304         whenever(authController.isFingerprintEnrolled(userId)).thenReturn(true)
305     }
306 
307     @Test
308     fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() =
309         testScope.runTest {
310             createBiometricSettingsRepository()
311             faceAuthIsEnabledByBiometricManager()
312 
313             doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
314 
315             runCurrent()
316             clearInvocations(authController)
317 
318             whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
319             val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
320 
321             assertThat(faceAuthAllowed()).isFalse()
322             verify(authController).addCallback(authControllerCallback.capture())
323             enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true)
324 
325             assertThat(faceAuthAllowed()).isFalse()
326 
327             enrollmentChange(SIDE_FINGERPRINT, PRIMARY_USER_ID, true)
328 
329             assertThat(faceAuthAllowed()).isFalse()
330 
331             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
332 
333             assertThat(faceAuthAllowed()).isFalse()
334 
335             whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
336 
337             enrollmentChange(FACE, ANOTHER_USER_ID, true)
338 
339             assertThat(faceAuthAllowed()).isFalse()
340 
341             faceAuthIsEnrolled()
342 
343             enrollmentChange(FACE, PRIMARY_USER_ID, true)
344 
345             assertThat(faceAuthAllowed()).isTrue()
346         }
347 
348     private fun faceAuthIsEnabledByBiometricManager(userId: Int = PRIMARY_USER_ID) {
349         verify(biometricManager, atLeastOnce())
350             .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
351         biometricManagerCallback.value.onChanged(true, userId)
352     }
353 
354     @Test
355     fun faceEnrollmentStatusOfNewUserUponUserSwitch() =
356         testScope.runTest {
357             createBiometricSettingsRepository()
358             runCurrent()
359             clearInvocations(authController)
360 
361             whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
362             whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
363             val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
364 
365             assertThat(faceAuthAllowed()).isFalse()
366         }
367 
368     @Test
369     fun faceEnrollmentChangesArePropagatedAfterUserSwitch() =
370         testScope.runTest {
371             createBiometricSettingsRepository()
372             verify(biometricManager)
373                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
374 
375             userRepository.setSelectedUserInfo(ANOTHER_USER)
376             doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID)
377             biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
378 
379             runCurrent()
380             clearInvocations(authController)
381 
382             val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
383             runCurrent()
384 
385             verify(authController).addCallback(authControllerCallback.capture())
386 
387             whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
388             enrollmentChange(FACE, ANOTHER_USER_ID, true)
389 
390             assertThat(faceAuthAllowed()).isTrue()
391         }
392 
393     @Test
394     fun devicePolicyControlsFaceAuthenticationEnabledState() =
395         testScope.runTest {
396             faceAuthIsEnrolled()
397 
398             createBiometricSettingsRepository()
399             verify(biometricManager)
400                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
401 
402             whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
403                 .thenReturn(KEYGUARD_DISABLE_FINGERPRINT or KEYGUARD_DISABLE_FACE)
404 
405             val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
406             runCurrent()
407 
408             broadcastDPMStateChange()
409 
410             assertThat(isFaceAuthAllowed()).isFalse()
411 
412             biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
413             runCurrent()
414             assertThat(isFaceAuthAllowed()).isFalse()
415 
416             whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
417                 .thenReturn(KEYGUARD_DISABLE_FINGERPRINT)
418             broadcastDPMStateChange()
419 
420             assertThat(isFaceAuthAllowed()).isTrue()
421         }
422 
423     @Test
424     fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
425         testScope.runTest {
426             faceAuthIsEnrolled()
427 
428             createBiometricSettingsRepository()
429             verify(biometricManager)
430                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
431             whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
432                 .thenReturn(0)
433             broadcastDPMStateChange()
434             val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
435 
436             assertThat(isFaceAuthAllowed()).isFalse()
437 
438             // Value changes for another user
439             biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
440 
441             assertThat(isFaceAuthAllowed()).isFalse()
442 
443             // Value changes for current user.
444             biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
445 
446             assertThat(isFaceAuthAllowed()).isTrue()
447         }
448 
449     private fun faceAuthIsEnrolled(userId: Int = PRIMARY_USER_ID) {
450         whenever(authController.isFaceAuthEnrolled(userId)).thenReturn(true)
451     }
452 
453     @Test
454     fun userChange_biometricEnabledChange_handlesRaceCondition() =
455         testScope.runTest {
456             createBiometricSettingsRepository()
457             whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
458 
459             verify(biometricManager)
460                 .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
461             val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
462             biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
463             runCurrent()
464 
465             userRepository.setSelectedUserInfo(ANOTHER_USER)
466             runCurrent()
467 
468             assertThat(isFaceAuthAllowed()).isTrue()
469         }
470 
471     @Test
472     fun biometricManagerCallbackIsRegisteredOnlyOnce() =
473         testScope.runTest {
474             createBiometricSettingsRepository()
475 
476             collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)()
477             collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)()
478             collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)()
479 
480             verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any())
481         }
482 
483     @Test
484     fun faceAuthIsAlwaysSupportedIfSpecificPostureIsNotConfigured() =
485         testScope.runTest {
486             overrideResource(
487                 R.integer.config_face_auth_supported_posture,
488                 DevicePostureController.DEVICE_POSTURE_UNKNOWN
489             )
490 
491             createBiometricSettingsRepository()
492 
493             assertThat(collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)()).isTrue()
494         }
495 
496     @Test
497     fun faceAuthIsSupportedOnlyWhenDevicePostureMatchesConfigValue() =
498         testScope.runTest {
499             overrideResource(
500                 R.integer.config_face_auth_supported_posture,
501                 DevicePostureController.DEVICE_POSTURE_FLIPPED
502             )
503 
504             createBiometricSettingsRepository()
505 
506             val isFaceAuthSupported =
507                 collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)
508 
509             assertThat(isFaceAuthSupported()).isFalse()
510 
511             devicePostureRepository.setCurrentPosture(DevicePosture.CLOSED)
512             assertThat(isFaceAuthSupported()).isFalse()
513 
514             devicePostureRepository.setCurrentPosture(DevicePosture.HALF_OPENED)
515             assertThat(isFaceAuthSupported()).isFalse()
516 
517             devicePostureRepository.setCurrentPosture(DevicePosture.OPENED)
518             assertThat(isFaceAuthSupported()).isFalse()
519 
520             devicePostureRepository.setCurrentPosture(DevicePosture.UNKNOWN)
521             assertThat(isFaceAuthSupported()).isFalse()
522 
523             devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED)
524             assertThat(isFaceAuthSupported()).isTrue()
525         }
526 
527     @Test
528     fun userInLockdownUsesAuthFlagsToDetermineValue() =
529         testScope.runTest {
530             createBiometricSettingsRepository()
531 
532             val isUserInLockdown = collectLastValue(underTest.isCurrentUserInLockdown)
533             // has default value.
534             assertThat(isUserInLockdown()).isFalse()
535 
536             // change strong auth flags for another user.
537             // Combine with one more flag to check if we do the bitwise and
538             val inLockdown =
539                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
540             onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
541 
542             // Still false.
543             assertThat(isUserInLockdown()).isFalse()
544 
545             // change strong auth flags for current user.
546             onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
547 
548             assertThat(isUserInLockdown()).isTrue()
549         }
550 
551     @Test
552     fun authFlagChangesForCurrentUserArePropagated() =
553         testScope.runTest {
554             createBiometricSettingsRepository()
555 
556             val authFlags = collectLastValue(underTest.authenticationFlags)
557             // has default value.
558             val defaultStrongAuthValue = STRONG_AUTH_REQUIRED_AFTER_BOOT
559             assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
560 
561             // change strong auth flags for another user.
562             // Combine with one more flag to check if we do the bitwise and
563             val inLockdown =
564                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
565             onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
566 
567             // Still false.
568             assertThat(authFlags()!!.flag).isEqualTo(defaultStrongAuthValue)
569 
570             // change strong auth flags for current user.
571             onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
572 
573             assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
574 
575             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, ANOTHER_USER_ID)
576 
577             assertThat(authFlags()!!.flag).isEqualTo(inLockdown)
578 
579             userRepository.setSelectedUserInfo(ANOTHER_USER)
580             assertThat(authFlags()!!.flag).isEqualTo(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
581         }
582 
583     @Test
584     fun faceAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFaceIsClass3() =
585         testScope.runTest {
586             createBiometricSettingsRepository()
587             val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
588 
589             faceAuthIsEnrolled()
590             deviceIsInPostureThatSupportsFaceAuth()
591             doNotDisableKeyguardAuthFeatures()
592             faceAuthIsStrongBiometric()
593             faceAuthIsEnabledByBiometricManager()
594 
595             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
596             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
597 
598             assertThat(isFaceAuthCurrentlyAllowed).isTrue()
599 
600             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID)
601             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
602 
603             assertThat(isFaceAuthCurrentlyAllowed).isFalse()
604         }
605 
606     @Test
607     fun faceAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFaceIsNotStrong() =
608         testScope.runTest {
609             createBiometricSettingsRepository()
610             val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
611 
612             faceAuthIsEnrolled()
613             deviceIsInPostureThatSupportsFaceAuth()
614             doNotDisableKeyguardAuthFeatures()
615             faceAuthIsNonStrongBiometric()
616             faceAuthIsEnabledByBiometricManager()
617 
618             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
619             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
620 
621             assertThat(isFaceAuthCurrentlyAllowed).isFalse()
622 
623             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID)
624             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
625 
626             assertThat(isFaceAuthCurrentlyAllowed).isFalse()
627 
628             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
629             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
630 
631             assertThat(isFaceAuthCurrentlyAllowed).isTrue()
632         }
633 
634     @Test
635     fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() =
636         testScope.runTest {
637             createBiometricSettingsRepository()
638             val isFingerprintCurrentlyAllowed by
639                 collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
640 
641             fingerprintIsEnrolled(PRIMARY_USER_ID)
642             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
643             doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
644             runCurrent()
645 
646             fingerprintPropertyRepository.setProperties(
647                 1,
648                 SensorStrength.STRONG,
649                 FingerprintSensorType.UDFPS_OPTICAL,
650                 emptyMap()
651             )
652             // Non strong auth is not allowed now, FP is marked strong
653             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
654             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
655 
656             assertThat(isFingerprintCurrentlyAllowed).isTrue()
657 
658             fingerprintPropertyRepository.setProperties(
659                 1,
660                 SensorStrength.CONVENIENCE,
661                 FingerprintSensorType.UDFPS_OPTICAL,
662                 emptyMap()
663             )
664             assertThat(isFingerprintCurrentlyAllowed).isFalse()
665 
666             fingerprintPropertyRepository.setProperties(
667                 1,
668                 SensorStrength.WEAK,
669                 FingerprintSensorType.UDFPS_OPTICAL,
670                 emptyMap()
671             )
672             assertThat(isFingerprintCurrentlyAllowed).isFalse()
673         }
674 
675     @Test
676     fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() =
677         testScope.runTest {
678             createBiometricSettingsRepository()
679             val isFingerprintCurrentlyAllowed by
680                 collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
681 
682             fingerprintIsEnrolled(PRIMARY_USER_ID)
683             enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
684             doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
685             runCurrent()
686 
687             fingerprintPropertyRepository.setProperties(
688                 1,
689                 SensorStrength.STRONG,
690                 FingerprintSensorType.UDFPS_OPTICAL,
691                 emptyMap()
692             )
693             // Non strong auth is not allowed now, FP is marked strong
694             onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID)
695             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
696 
697             assertThat(isFingerprintCurrentlyAllowed).isFalse()
698 
699             onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
700             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
701 
702             assertThat(isFingerprintCurrentlyAllowed).isTrue()
703         }
704 
705     private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
706         authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
707     }
708 
709     private fun doNotDisableKeyguardAuthFeatures(userId: Int = PRIMARY_USER_ID) {
710         whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(userId)))
711             .thenReturn(0)
712         broadcastDPMStateChange()
713     }
714 
715     private fun broadcastDPMStateChange() {
716         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
717             context,
718             Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
719         )
720     }
721 
722     companion object {
723         private const val PRIMARY_USER_ID = 0
724         private val PRIMARY_USER =
725             UserInfo(
726                 /* id= */ PRIMARY_USER_ID,
727                 /* name= */ "primary user",
728                 /* flags= */ UserInfo.FLAG_PRIMARY
729             )
730 
731         private const val ANOTHER_USER_ID = 1
732         private val ANOTHER_USER =
733             UserInfo(
734                 /* id= */ ANOTHER_USER_ID,
735                 /* name= */ "another user",
736                 /* flags= */ UserInfo.FLAG_PRIMARY
737             )
738     }
739 }
740