1 /* <lambda>null2 * 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.biometrics.shared.model.AuthenticationReason 26 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 27 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 28 import com.android.systemui.dagger.SysUISingleton 29 import com.android.systemui.dagger.qualifiers.Application 30 import com.android.systemui.dagger.qualifiers.Main 31 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus 32 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus 33 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus 34 import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus 35 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus 36 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 37 import javax.inject.Inject 38 import kotlinx.coroutines.CoroutineDispatcher 39 import kotlinx.coroutines.CoroutineScope 40 import kotlinx.coroutines.channels.awaitClose 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.flow.SharingStarted 43 import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed 44 import kotlinx.coroutines.flow.StateFlow 45 import kotlinx.coroutines.flow.buffer 46 import kotlinx.coroutines.flow.filterNotNull 47 import kotlinx.coroutines.flow.flowOf 48 import kotlinx.coroutines.flow.flowOn 49 import kotlinx.coroutines.flow.map 50 import kotlinx.coroutines.flow.shareIn 51 import kotlinx.coroutines.flow.stateIn 52 53 /** Encapsulates state about device entry fingerprint auth mechanism. */ 54 interface DeviceEntryFingerprintAuthRepository { 55 /** Whether the device entry fingerprint auth is locked out. */ 56 val isLockedOut: Flow<Boolean> 57 58 /** 59 * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is 60 * actively authenticating. 61 */ 62 val isRunning: Flow<Boolean> 63 64 /** Whether the fingerprint sensor is actively authenticating. */ 65 val isEngaged: StateFlow<Boolean> 66 67 /** 68 * Fingerprint sensor type present on the device, null if fingerprint sensor is not available. 69 */ 70 val availableFpSensorType: Flow<BiometricType?> 71 72 /** Provide the current status of fingerprint authentication. */ 73 val authenticationStatus: Flow<FingerprintAuthenticationStatus> 74 75 /** Indicates whether to update the side fingerprint sensor indicator visibility. */ 76 val shouldUpdateIndicatorVisibility: Flow<Boolean> 77 } 78 79 /** 80 * Implementation of [DeviceEntryFingerprintAuthRepository] that uses [KeyguardUpdateMonitor] as the 81 * source of truth. 82 * 83 * Dependency on [KeyguardUpdateMonitor] will be removed once fingerprint auth state is moved out of 84 * [KeyguardUpdateMonitor] 85 */ 86 @SysUISingleton 87 class DeviceEntryFingerprintAuthRepositoryImpl 88 @Inject 89 constructor( 90 val authController: AuthController, 91 val keyguardUpdateMonitor: KeyguardUpdateMonitor, 92 @Application scope: CoroutineScope, 93 @Main private val mainDispatcher: CoroutineDispatcher, 94 ) : DeviceEntryFingerprintAuthRepository { 95 96 override val availableFpSensorType: Flow<BiometricType?> 97 get() { 98 return if (authController.areAllFingerprintAuthenticatorsRegistered()) { 99 flowOf(getFpSensorType()) 100 } else { <lambda>null101 conflatedCallbackFlow { 102 val callback = 103 object : AuthController.Callback { 104 override fun onAllAuthenticatorsRegistered(@Modality modality: Int) { 105 if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) 106 trySendWithFailureLogging( 107 getFpSensorType(), 108 TAG, 109 "onAllAuthenticatorsRegistered, emitting fpSensorType" 110 ) 111 } 112 } 113 authController.addCallback(callback) 114 trySendWithFailureLogging( 115 getFpSensorType(), 116 TAG, 117 "initial value for fpSensorType" 118 ) 119 awaitClose { authController.removeCallback(callback) } 120 } 121 } 122 } 123 getFpSensorTypenull124 private fun getFpSensorType(): BiometricType? { 125 return if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT 126 else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT 127 else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null 128 } 129 130 override val isLockedOut: Flow<Boolean> = <lambda>null131 conflatedCallbackFlow { 132 val sendLockoutUpdate = 133 fun() { 134 trySendWithFailureLogging( 135 keyguardUpdateMonitor.isFingerprintLockedOut, 136 TAG, 137 "onLockedOutStateChanged" 138 ) 139 } 140 val callback = 141 object : KeyguardUpdateMonitorCallback() { 142 override fun onLockedOutStateChanged( 143 biometricSourceType: BiometricSourceType? 144 ) { 145 if (biometricSourceType == BiometricSourceType.FINGERPRINT) { 146 sendLockoutUpdate() 147 } 148 } 149 } 150 keyguardUpdateMonitor.registerCallback(callback) 151 sendLockoutUpdate() 152 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 153 } 154 .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false) 155 156 override val isRunning: Flow<Boolean> 157 get() = <lambda>null158 conflatedCallbackFlow { 159 val callback = 160 object : KeyguardUpdateMonitorCallback() { 161 override fun onBiometricRunningStateChanged( 162 running: Boolean, 163 biometricSourceType: BiometricSourceType? 164 ) { 165 if (biometricSourceType == BiometricSourceType.FINGERPRINT) { 166 trySendWithFailureLogging( 167 running, 168 TAG, 169 "Fingerprint running state changed" 170 ) 171 } 172 } 173 } 174 keyguardUpdateMonitor.registerCallback(callback) 175 trySendWithFailureLogging( 176 keyguardUpdateMonitor.isFingerprintDetectionRunning, 177 TAG, 178 "Initial fingerprint running state" 179 ) 180 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 181 } 182 .flowOn( 183 mainDispatcher 184 ) // keyguardUpdateMonitor requires registration on main thread. 185 186 override val isEngaged: StateFlow<Boolean> = 187 authenticationStatus <lambda>null188 .map { it.isEngaged } 189 .filterNotNull() <lambda>null190 .map { it } 191 .stateIn( 192 scope = scope, 193 started = WhileSubscribed(), 194 initialValue = false, 195 ) 196 197 // TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages 198 // in BiometricStatusRepository 199 /** 200 * FingerprintAuthenticationStatus Multiple statuses may arrive in immediate sequence (ie: 201 * acquired, failed, help, error), so we use a buffer to ensure consumers receive each distinct 202 * status. 203 */ 204 override val authenticationStatus: Flow<FingerprintAuthenticationStatus> 205 get() = <lambda>null206 conflatedCallbackFlow { 207 val callback = 208 object : KeyguardUpdateMonitorCallback() { 209 override fun onBiometricAuthenticated( 210 userId: Int, 211 biometricSourceType: BiometricSourceType, 212 isStrongBiometric: Boolean, 213 ) { 214 sendUpdateIfFingerprint( 215 biometricSourceType, 216 SuccessFingerprintAuthenticationStatus( 217 userId, 218 isStrongBiometric, 219 ), 220 ) 221 } 222 223 override fun onBiometricError( 224 msgId: Int, 225 errString: String?, 226 biometricSourceType: BiometricSourceType, 227 ) { 228 sendUpdateIfFingerprint( 229 biometricSourceType, 230 ErrorFingerprintAuthenticationStatus( 231 msgId, 232 errString, 233 ), 234 ) 235 } 236 237 override fun onBiometricHelp( 238 msgId: Int, 239 helpString: String?, 240 biometricSourceType: BiometricSourceType, 241 ) { 242 sendUpdateIfFingerprint( 243 biometricSourceType, 244 HelpFingerprintAuthenticationStatus( 245 msgId, 246 helpString, 247 ), 248 ) 249 } 250 251 override fun onBiometricAuthFailed( 252 biometricSourceType: BiometricSourceType, 253 ) { 254 sendUpdateIfFingerprint( 255 biometricSourceType, 256 FailFingerprintAuthenticationStatus, 257 ) 258 } 259 260 override fun onBiometricAcquired( 261 biometricSourceType: BiometricSourceType, 262 acquireInfo: Int, 263 ) { 264 sendUpdateIfFingerprint( 265 biometricSourceType, 266 AcquiredFingerprintAuthenticationStatus( 267 AuthenticationReason.DeviceEntryAuthentication, 268 acquireInfo 269 ), 270 ) 271 } 272 273 private fun sendUpdateIfFingerprint( 274 biometricSourceType: BiometricSourceType, 275 authenticationStatus: FingerprintAuthenticationStatus 276 ) { 277 if (biometricSourceType != BiometricSourceType.FINGERPRINT) { 278 return 279 } 280 trySendWithFailureLogging( 281 authenticationStatus, 282 TAG, 283 "new fingerprint authentication status" 284 ) 285 } 286 } 287 keyguardUpdateMonitor.registerCallback(callback) 288 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 289 } 290 .buffer(capacity = 4) 291 292 override val shouldUpdateIndicatorVisibility: Flow<Boolean> = <lambda>null293 conflatedCallbackFlow { 294 val sendShouldUpdateIndicatorVisibility = 295 { shouldUpdateIndicatorVisibility: Boolean -> 296 trySendWithFailureLogging( 297 shouldUpdateIndicatorVisibility, 298 TAG, 299 "Error sending shouldUpdateIndicatorVisibility " + 300 "$shouldUpdateIndicatorVisibility" 301 ) 302 } 303 304 val callback = 305 object : KeyguardUpdateMonitorCallback() { 306 override fun onBiometricRunningStateChanged( 307 running: Boolean, 308 biometricSourceType: BiometricSourceType? 309 ) { 310 sendShouldUpdateIndicatorVisibility(true) 311 } 312 override fun onStrongAuthStateChanged(userId: Int) { 313 sendShouldUpdateIndicatorVisibility(true) 314 } 315 } 316 sendShouldUpdateIndicatorVisibility(false) 317 keyguardUpdateMonitor.registerCallback(callback) 318 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 319 } 320 .flowOn(mainDispatcher) 321 .shareIn(scope, started = SharingStarted.WhileSubscribed(), replay = 1) 322 323 companion object { 324 const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" 325 } 326 } 327