• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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