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