1 /* 2 * Copyright (C) 2023 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.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT 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.coroutines.collectLastValue 29 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus 30 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus 31 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus 32 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus 33 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 34 import com.android.systemui.util.mockito.whenever 35 import com.google.common.truth.Truth.assertThat 36 import kotlinx.coroutines.ExperimentalCoroutinesApi 37 import kotlinx.coroutines.test.TestScope 38 import kotlinx.coroutines.test.runCurrent 39 import kotlinx.coroutines.test.runTest 40 import org.junit.Before 41 import org.junit.Test 42 import org.junit.runner.RunWith 43 import org.mockito.ArgumentCaptor 44 import org.mockito.Captor 45 import org.mockito.Mock 46 import org.mockito.Mockito.atLeastOnce 47 import org.mockito.Mockito.verify 48 import org.mockito.MockitoAnnotations 49 50 @OptIn(ExperimentalCoroutinesApi::class) 51 @SmallTest 52 @RoboPilotTest 53 @RunWith(AndroidJUnit4::class) 54 class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { 55 @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor 56 @Mock private lateinit var authController: AuthController 57 @Captor 58 private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback> 59 60 private lateinit var testScope: TestScope 61 62 private lateinit var underTest: DeviceEntryFingerprintAuthRepository 63 64 @Before 65 fun setUp() { 66 MockitoAnnotations.initMocks(this) 67 testScope = TestScope() 68 69 underTest = 70 DeviceEntryFingerprintAuthRepositoryImpl( 71 authController, 72 keyguardUpdateMonitor, 73 testScope.backgroundScope, 74 ) 75 } 76 77 @Test 78 fun isLockedOut_whenFingerprintLockoutStateChanges_emitsNewValue() = 79 testScope.runTest { 80 val isLockedOutValue = collectLastValue(underTest.isLockedOut) 81 runCurrent() 82 83 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 84 val callback = updateMonitorCallback.value 85 whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true) 86 87 callback.onLockedOutStateChanged(BiometricSourceType.FACE) 88 assertThat(isLockedOutValue()).isFalse() 89 90 callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT) 91 assertThat(isLockedOutValue()).isTrue() 92 93 whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) 94 callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT) 95 assertThat(isLockedOutValue()).isFalse() 96 } 97 98 @Test 99 fun fpRunningStateIsPropagated() = 100 testScope.runTest { 101 val isRunning = collectLastValue(underTest.isRunning) 102 whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true) 103 104 // Initial value is available 105 assertThat(isRunning()).isTrue() 106 107 verify(keyguardUpdateMonitor, atLeastOnce()) 108 .registerCallback(updateMonitorCallback.capture()) 109 invokeOnCallback { 110 it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT) 111 } 112 113 assertThat(isRunning()).isFalse() 114 115 invokeOnCallback { it.onBiometricRunningStateChanged(true, BiometricSourceType.FACE) } 116 117 assertThat(isRunning()).isFalse() 118 119 updateMonitorCallback.value.onBiometricRunningStateChanged( 120 true, 121 BiometricSourceType.FINGERPRINT 122 ) 123 assertThat(isRunning()).isTrue() 124 } 125 126 private fun invokeOnCallback(action: (KeyguardUpdateMonitorCallback) -> Unit) { 127 updateMonitorCallback.allValues.forEach { action(it) } 128 } 129 130 @Test 131 fun enabledFingerprintTypeProvidesTheCorrectOutputForSpfs() = 132 testScope.runTest { 133 whenever(authController.isSfpsSupported).thenReturn(true) 134 whenever(authController.isUdfpsSupported).thenReturn(false) 135 whenever(authController.isRearFpsSupported).thenReturn(false) 136 137 val availableFpSensorType = collectLastValue(underTest.availableFpSensorType) 138 assertThat(availableFpSensorType()).isEqualTo(BiometricType.SIDE_FINGERPRINT) 139 } 140 141 @Test 142 fun enabledFingerprintTypeProvidesTheCorrectOutputForUdfps() = 143 testScope.runTest { 144 whenever(authController.isSfpsSupported).thenReturn(false) 145 whenever(authController.isUdfpsSupported).thenReturn(true) 146 whenever(authController.isRearFpsSupported).thenReturn(false) 147 val availableFpSensorType = collectLastValue(underTest.availableFpSensorType) 148 assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT) 149 } 150 151 @Test 152 fun enabledFingerprintTypeProvidesTheCorrectOutputForRearFps() = 153 testScope.runTest { 154 whenever(authController.isSfpsSupported).thenReturn(false) 155 whenever(authController.isUdfpsSupported).thenReturn(false) 156 whenever(authController.isRearFpsSupported).thenReturn(true) 157 158 val availableFpSensorType = collectLastValue(underTest.availableFpSensorType) 159 160 assertThat(availableFpSensorType()).isEqualTo(BiometricType.REAR_FINGERPRINT) 161 } 162 163 @Test 164 fun enabledFingerprintTypeProvidesTheCorrectOutputAfterAllAuthenticatorsAreRegistered() = 165 testScope.runTest { 166 whenever(authController.isSfpsSupported).thenReturn(false) 167 whenever(authController.isUdfpsSupported).thenReturn(false) 168 whenever(authController.isRearFpsSupported).thenReturn(false) 169 whenever(authController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(false) 170 171 val availableFpSensorType = collectLastValue(underTest.availableFpSensorType) 172 runCurrent() 173 174 val callback = ArgumentCaptor.forClass(AuthController.Callback::class.java) 175 verify(authController).addCallback(callback.capture()) 176 assertThat(availableFpSensorType()).isNull() 177 178 whenever(authController.isUdfpsSupported).thenReturn(true) 179 callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT) 180 assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT) 181 } 182 183 @Test 184 fun onFingerprintSuccess_successAuthenticationStatus() = 185 testScope.runTest { 186 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 187 runCurrent() 188 189 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 190 updateMonitorCallback.value.onBiometricAuthenticated( 191 0, 192 BiometricSourceType.FINGERPRINT, 193 true, 194 ) 195 196 val status = authenticationStatus as SuccessFingerprintAuthenticationStatus 197 assertThat(status.userId).isEqualTo(0) 198 assertThat(status.isStrongBiometric).isEqualTo(true) 199 } 200 201 @Test 202 fun onFingerprintFailed_failedAuthenticationStatus() = 203 testScope.runTest { 204 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 205 runCurrent() 206 207 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 208 updateMonitorCallback.value.onBiometricAuthFailed( 209 BiometricSourceType.FINGERPRINT, 210 ) 211 212 assertThat(authenticationStatus) 213 .isInstanceOf(FailFingerprintAuthenticationStatus::class.java) 214 } 215 216 @Test 217 fun onFingerprintError_errorAuthenticationStatus() = 218 testScope.runTest { 219 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 220 runCurrent() 221 222 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 223 updateMonitorCallback.value.onBiometricError( 224 1, 225 "test_string", 226 BiometricSourceType.FINGERPRINT, 227 ) 228 229 val status = authenticationStatus as ErrorFingerprintAuthenticationStatus 230 assertThat(status.msgId).isEqualTo(1) 231 assertThat(status.msg).isEqualTo("test_string") 232 } 233 234 @Test 235 fun onFingerprintHelp_helpAuthenticationStatus() = 236 testScope.runTest { 237 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 238 runCurrent() 239 240 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 241 updateMonitorCallback.value.onBiometricHelp( 242 1, 243 "test_string", 244 BiometricSourceType.FINGERPRINT, 245 ) 246 247 val status = authenticationStatus as HelpFingerprintAuthenticationStatus 248 assertThat(status.msgId).isEqualTo(1) 249 assertThat(status.msg).isEqualTo("test_string") 250 } 251 252 @Test 253 fun onFingerprintAcquired_acquiredAuthenticationStatus() = 254 testScope.runTest { 255 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 256 runCurrent() 257 258 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 259 updateMonitorCallback.value.onBiometricAcquired( 260 BiometricSourceType.FINGERPRINT, 261 5, 262 ) 263 264 val status = authenticationStatus as AcquiredFingerprintAuthenticationStatus 265 assertThat(status.acquiredInfo).isEqualTo(5) 266 } 267 268 @Test 269 fun onFaceCallbacks_fingerprintAuthenticationStatusIsUnchanged() = 270 testScope.runTest { 271 val authenticationStatus by collectLastValue(underTest.authenticationStatus) 272 runCurrent() 273 274 verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) 275 updateMonitorCallback.value.onBiometricAuthenticated( 276 0, 277 BiometricSourceType.FACE, 278 true, 279 ) 280 assertThat(authenticationStatus).isNull() 281 282 updateMonitorCallback.value.onBiometricAuthFailed( 283 BiometricSourceType.FACE, 284 ) 285 assertThat(authenticationStatus).isNull() 286 287 updateMonitorCallback.value.onBiometricHelp( 288 1, 289 "test_string", 290 BiometricSourceType.FACE, 291 ) 292 assertThat(authenticationStatus).isNull() 293 294 updateMonitorCallback.value.onBiometricAcquired( 295 BiometricSourceType.FACE, 296 5, 297 ) 298 assertThat(authenticationStatus).isNull() 299 300 updateMonitorCallback.value.onBiometricError( 301 1, 302 "test_string", 303 BiometricSourceType.FACE, 304 ) 305 assertThat(authenticationStatus).isNull() 306 } 307 } 308