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