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.utils.coroutines.flow.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.Companion.Eagerly 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: StateFlow<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 <lambda>null130 override val isLockedOut: StateFlow<Boolean> by lazy { 131 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( 155 scope, 156 started = Eagerly, 157 initialValue = keyguardUpdateMonitor.isFingerprintLockedOut, 158 ) 159 } 160 161 override val isRunning: Flow<Boolean> 162 get() = <lambda>null163 conflatedCallbackFlow { 164 val callback = 165 object : KeyguardUpdateMonitorCallback() { 166 override fun onBiometricRunningStateChanged( 167 running: Boolean, 168 biometricSourceType: BiometricSourceType?, 169 ) { 170 if (biometricSourceType == BiometricSourceType.FINGERPRINT) { 171 trySendWithFailureLogging( 172 running, 173 TAG, 174 "Fingerprint running state changed", 175 ) 176 } 177 } 178 } 179 keyguardUpdateMonitor.registerCallback(callback) 180 trySendWithFailureLogging( 181 keyguardUpdateMonitor.isFingerprintDetectionRunning, 182 TAG, 183 "Initial fingerprint running state", 184 ) 185 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 186 } 187 .flowOn( 188 mainDispatcher 189 ) // keyguardUpdateMonitor requires registration on main thread. 190 191 override val isEngaged: StateFlow<Boolean> = 192 authenticationStatus <lambda>null193 .map { it.isEngaged } 194 .filterNotNull() <lambda>null195 .map { it } 196 .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false) 197 198 // TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages 199 // in BiometricStatusRepository 200 /** 201 * FingerprintAuthenticationStatus Multiple statuses may arrive in immediate sequence (ie: 202 * acquired, failed, help, error), so we use a buffer to ensure consumers receive each distinct 203 * status. 204 */ 205 override val authenticationStatus: Flow<FingerprintAuthenticationStatus> 206 get() = <lambda>null207 conflatedCallbackFlow { 208 val callback = 209 object : KeyguardUpdateMonitorCallback() { 210 override fun onBiometricAuthenticated( 211 userId: Int, 212 biometricSourceType: BiometricSourceType, 213 isStrongBiometric: Boolean, 214 ) { 215 sendUpdateIfFingerprint( 216 biometricSourceType, 217 SuccessFingerprintAuthenticationStatus( 218 userId, 219 isStrongBiometric, 220 ), 221 ) 222 } 223 224 override fun onBiometricError( 225 msgId: Int, 226 errString: String?, 227 biometricSourceType: BiometricSourceType, 228 ) { 229 sendUpdateIfFingerprint( 230 biometricSourceType, 231 ErrorFingerprintAuthenticationStatus(msgId, errString), 232 ) 233 } 234 235 override fun onBiometricHelp( 236 msgId: Int, 237 helpString: String?, 238 biometricSourceType: BiometricSourceType, 239 ) { 240 sendUpdateIfFingerprint( 241 biometricSourceType, 242 HelpFingerprintAuthenticationStatus(msgId, helpString), 243 ) 244 } 245 246 override fun onBiometricAuthFailed( 247 biometricSourceType: BiometricSourceType 248 ) { 249 sendUpdateIfFingerprint( 250 biometricSourceType, 251 FailFingerprintAuthenticationStatus, 252 ) 253 } 254 255 override fun onBiometricAcquired( 256 biometricSourceType: BiometricSourceType, 257 acquireInfo: Int, 258 ) { 259 sendUpdateIfFingerprint( 260 biometricSourceType, 261 AcquiredFingerprintAuthenticationStatus( 262 AuthenticationReason.DeviceEntryAuthentication, 263 acquireInfo, 264 ), 265 ) 266 } 267 268 private fun sendUpdateIfFingerprint( 269 biometricSourceType: BiometricSourceType, 270 authenticationStatus: FingerprintAuthenticationStatus, 271 ) { 272 if (biometricSourceType != BiometricSourceType.FINGERPRINT) { 273 return 274 } 275 trySendWithFailureLogging( 276 authenticationStatus, 277 TAG, 278 "new fingerprint authentication status", 279 ) 280 } 281 } 282 keyguardUpdateMonitor.registerCallback(callback) 283 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 284 } 285 .flowOn(mainDispatcher) 286 .buffer(capacity = 4) 287 288 override val shouldUpdateIndicatorVisibility: Flow<Boolean> = <lambda>null289 conflatedCallbackFlow { 290 val sendShouldUpdateIndicatorVisibility = 291 { shouldUpdateIndicatorVisibility: Boolean -> 292 trySendWithFailureLogging( 293 shouldUpdateIndicatorVisibility, 294 TAG, 295 "Error sending shouldUpdateIndicatorVisibility " + 296 "$shouldUpdateIndicatorVisibility", 297 ) 298 } 299 300 val callback = 301 object : KeyguardUpdateMonitorCallback() { 302 override fun onBiometricRunningStateChanged( 303 running: Boolean, 304 biometricSourceType: BiometricSourceType?, 305 ) { 306 sendShouldUpdateIndicatorVisibility(true) 307 } 308 309 override fun onStrongAuthStateChanged(userId: Int) { 310 sendShouldUpdateIndicatorVisibility(true) 311 } 312 } 313 sendShouldUpdateIndicatorVisibility(false) 314 keyguardUpdateMonitor.registerCallback(callback) 315 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 316 } 317 .flowOn(mainDispatcher) 318 .shareIn(scope, started = WhileSubscribed(), replay = 1) 319 320 companion object { 321 const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" 322 } 323 } 324