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