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 20 import android.hardware.biometrics.BiometricAuthenticator.Modality 21 import android.hardware.biometrics.BiometricSourceType 22 import com.android.keyguard.KeyguardUpdateMonitor 23 import com.android.keyguard.KeyguardUpdateMonitorCallback 24 import com.android.systemui.biometrics.AuthController 25 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 26 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 27 import com.android.systemui.dagger.SysUISingleton 28 import com.android.systemui.dagger.qualifiers.Application 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.FingerprintAuthenticationStatus 33 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus 34 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 35 import javax.inject.Inject 36 import kotlinx.coroutines.CoroutineScope 37 import kotlinx.coroutines.channels.awaitClose 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.SharingStarted 40 import kotlinx.coroutines.flow.flowOf 41 import kotlinx.coroutines.flow.stateIn 42 43 /** Encapsulates state about device entry fingerprint auth mechanism. */ 44 interface DeviceEntryFingerprintAuthRepository { 45 /** Whether the device entry fingerprint auth is locked out. */ 46 val isLockedOut: Flow<Boolean> 47 48 /** 49 * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is 50 * actively authenticating. 51 */ 52 val isRunning: Flow<Boolean> 53 54 /** 55 * Fingerprint sensor type present on the device, null if fingerprint sensor is not available. 56 */ 57 val availableFpSensorType: Flow<BiometricType?> 58 59 /** Provide the current status of fingerprint authentication. */ 60 val authenticationStatus: Flow<FingerprintAuthenticationStatus> 61 } 62 63 /** 64 * Implementation of [DeviceEntryFingerprintAuthRepository] that uses [KeyguardUpdateMonitor] as the 65 * source of truth. 66 * 67 * Dependency on [KeyguardUpdateMonitor] will be removed once fingerprint auth state is moved out of 68 * [KeyguardUpdateMonitor] 69 */ 70 @SysUISingleton 71 class DeviceEntryFingerprintAuthRepositoryImpl 72 @Inject 73 constructor( 74 val authController: AuthController, 75 val keyguardUpdateMonitor: KeyguardUpdateMonitor, 76 @Application scope: CoroutineScope, 77 ) : DeviceEntryFingerprintAuthRepository { 78 79 override val availableFpSensorType: Flow<BiometricType?> 80 get() { 81 return if (authController.areAllFingerprintAuthenticatorsRegistered()) { 82 flowOf(getFpSensorType()) 83 } else { 84 conflatedCallbackFlow { 85 val callback = 86 object : AuthController.Callback { 87 override fun onAllAuthenticatorsRegistered(@Modality modality: Int) { 88 if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) 89 trySendWithFailureLogging( 90 getFpSensorType(), 91 TAG, 92 "onAllAuthenticatorsRegistered, emitting fpSensorType" 93 ) 94 } 95 } 96 authController.addCallback(callback) 97 trySendWithFailureLogging( 98 getFpSensorType(), 99 TAG, 100 "initial value for fpSensorType" 101 ) 102 awaitClose { authController.removeCallback(callback) } 103 } 104 } 105 } 106 107 private fun getFpSensorType(): BiometricType? { 108 return if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT 109 else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT 110 else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null 111 } 112 113 override val isLockedOut: Flow<Boolean> = 114 conflatedCallbackFlow { 115 val sendLockoutUpdate = 116 fun() { 117 trySendWithFailureLogging( 118 keyguardUpdateMonitor.isFingerprintLockedOut, 119 TAG, 120 "onLockedOutStateChanged" 121 ) 122 } 123 val callback = 124 object : KeyguardUpdateMonitorCallback() { 125 override fun onLockedOutStateChanged( 126 biometricSourceType: BiometricSourceType? 127 ) { 128 if (biometricSourceType == BiometricSourceType.FINGERPRINT) { 129 sendLockoutUpdate() 130 } 131 } 132 } 133 keyguardUpdateMonitor.registerCallback(callback) 134 sendLockoutUpdate() 135 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 136 } 137 .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false) 138 139 override val isRunning: Flow<Boolean> 140 get() = conflatedCallbackFlow { 141 val callback = 142 object : KeyguardUpdateMonitorCallback() { 143 override fun onBiometricRunningStateChanged( 144 running: Boolean, 145 biometricSourceType: BiometricSourceType? 146 ) { 147 if (biometricSourceType == BiometricSourceType.FINGERPRINT) { 148 trySendWithFailureLogging( 149 running, 150 TAG, 151 "Fingerprint running state changed" 152 ) 153 } 154 } 155 } 156 keyguardUpdateMonitor.registerCallback(callback) 157 trySendWithFailureLogging( 158 keyguardUpdateMonitor.isFingerprintDetectionRunning, 159 TAG, 160 "Initial fingerprint running state" 161 ) 162 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 163 } 164 165 override val authenticationStatus: Flow<FingerprintAuthenticationStatus> 166 get() = conflatedCallbackFlow { 167 val callback = 168 object : KeyguardUpdateMonitorCallback() { 169 override fun onBiometricAuthenticated( 170 userId: Int, 171 biometricSourceType: BiometricSourceType, 172 isStrongBiometric: Boolean, 173 ) { 174 175 sendUpdateIfFingerprint( 176 biometricSourceType, 177 SuccessFingerprintAuthenticationStatus( 178 userId, 179 isStrongBiometric, 180 ), 181 ) 182 } 183 184 override fun onBiometricError( 185 msgId: Int, 186 errString: String?, 187 biometricSourceType: BiometricSourceType, 188 ) { 189 sendUpdateIfFingerprint( 190 biometricSourceType, 191 ErrorFingerprintAuthenticationStatus( 192 msgId, 193 errString, 194 ), 195 ) 196 } 197 198 override fun onBiometricHelp( 199 msgId: Int, 200 helpString: String?, 201 biometricSourceType: BiometricSourceType, 202 ) { 203 sendUpdateIfFingerprint( 204 biometricSourceType, 205 HelpFingerprintAuthenticationStatus( 206 msgId, 207 helpString, 208 ), 209 ) 210 } 211 212 override fun onBiometricAuthFailed( 213 biometricSourceType: BiometricSourceType, 214 ) { 215 sendUpdateIfFingerprint( 216 biometricSourceType, 217 FailFingerprintAuthenticationStatus, 218 ) 219 } 220 221 override fun onBiometricAcquired( 222 biometricSourceType: BiometricSourceType, 223 acquireInfo: Int, 224 ) { 225 sendUpdateIfFingerprint( 226 biometricSourceType, 227 AcquiredFingerprintAuthenticationStatus( 228 acquireInfo, 229 ), 230 ) 231 } 232 233 private fun sendUpdateIfFingerprint( 234 biometricSourceType: BiometricSourceType, 235 authenticationStatus: FingerprintAuthenticationStatus 236 ) { 237 if (biometricSourceType != BiometricSourceType.FINGERPRINT) { 238 return 239 } 240 241 trySendWithFailureLogging( 242 authenticationStatus, 243 TAG, 244 "new fingerprint authentication status" 245 ) 246 } 247 } 248 keyguardUpdateMonitor.registerCallback(callback) 249 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 250 } 251 252 companion object { 253 const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" 254 } 255 } 256