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.biometrics.data.repository
18 
19 import android.hardware.biometrics.AuthenticationStateListener
20 import android.hardware.biometrics.BiometricManager
21 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
22 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
23 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
24 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
25 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
26 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
27 import android.hardware.biometrics.BiometricSourceType
28 import android.hardware.biometrics.events.AuthenticationAcquiredInfo
29 import android.hardware.biometrics.events.AuthenticationErrorInfo
30 import android.hardware.biometrics.events.AuthenticationFailedInfo
31 import android.hardware.biometrics.events.AuthenticationHelpInfo
32 import android.hardware.biometrics.events.AuthenticationStartedInfo
33 import android.hardware.biometrics.events.AuthenticationStoppedInfo
34 import android.hardware.biometrics.events.AuthenticationSucceededInfo
35 import android.hardware.face.FaceManager
36 import android.hardware.fingerprint.FingerprintManager
37 import android.util.Log
38 import com.android.systemui.biometrics.shared.model.AuthenticationReason
39 import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
40 import com.android.systemui.biometrics.shared.model.AuthenticationState
41 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
42 import com.android.systemui.dagger.SysUISingleton
43 import com.android.systemui.dagger.qualifiers.Application
44 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
45 import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
46 import javax.inject.Inject
47 import kotlinx.coroutines.CoroutineScope
48 import kotlinx.coroutines.channels.awaitClose
49 import kotlinx.coroutines.flow.Flow
50 import kotlinx.coroutines.flow.SharingStarted
51 import kotlinx.coroutines.flow.callbackFlow
52 import kotlinx.coroutines.flow.distinctUntilChanged
53 import kotlinx.coroutines.flow.filter
54 import kotlinx.coroutines.flow.filterIsInstance
55 import kotlinx.coroutines.flow.map
56 import kotlinx.coroutines.flow.onEach
57 import kotlinx.coroutines.flow.shareIn
58 
59 /** A repository for the state of biometric authentication. */
60 interface BiometricStatusRepository {
61     /**
62      * The logical reason for the current fingerprint auth operation if one is on-going, otherwise
63      * [NotRunning].
64      */
65     val fingerprintAuthenticationReason: Flow<AuthenticationReason>
66 
67     /** The current status of an acquired fingerprint. */
68     val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus>
69 }
70 
71 @SysUISingleton
72 class BiometricStatusRepositoryImpl
73 @Inject
74 constructor(
75     @Application private val applicationScope: CoroutineScope,
76     private val biometricManager: BiometricManager?,
77 ) : BiometricStatusRepository {
78 
79     /**
80      * TODO(b/322555228): Replace usages of onAuthenticationError, onAuthenticationHelp,
81      *   onAuthenticationSucceeded, onAuthenticationFailed, onAuthenticationAcquired in
82      *   [FingerprintManager.AuthenticationCallback] and [FaceManager.AuthenticationCallback],
83      *   onDetectionError in [FingerprintManager.FingerprintDetectionCallback] and
84      *   [FaceManager.FaceDetectionCallback], and onEnrollmentError, onEnrollmentHelp, and
85      *   onAcquired in [FingerprintManager.EnrollmentCallback] and [FaceManager.EnrollmentCallback]
86      */
87     private val authenticationState: Flow<AuthenticationState> =
<lambda>null88         callbackFlow {
89                 val updateAuthenticationState = { state: AuthenticationState ->
90                     Log.d(TAG, "authenticationState updated: $state")
91                     trySendWithFailureLogging(state, TAG, "Error sending AuthenticationState state")
92                 }
93 
94                 val authenticationStateListener =
95                     object : AuthenticationStateListener.Stub() {
96                         override fun onAuthenticationAcquired(
97                             authInfo: AuthenticationAcquiredInfo
98                         ) {
99                             updateAuthenticationState(
100                                 AuthenticationState.Acquired(
101                                     authInfo.biometricSourceType,
102                                     authInfo.requestReason.toAuthenticationReason(),
103                                     authInfo.acquiredInfo
104                                 )
105                             )
106                         }
107 
108                         override fun onAuthenticationError(authInfo: AuthenticationErrorInfo) {
109                             updateAuthenticationState(
110                                 AuthenticationState.Error(
111                                     authInfo.biometricSourceType,
112                                     authInfo.errString,
113                                     authInfo.errCode,
114                                     authInfo.requestReason.toAuthenticationReason()
115                                 )
116                             )
117                         }
118 
119                         override fun onAuthenticationFailed(authInfo: AuthenticationFailedInfo) {
120                             updateAuthenticationState(
121                                 AuthenticationState.Failed(
122                                     authInfo.biometricSourceType,
123                                     authInfo.requestReason.toAuthenticationReason(),
124                                     authInfo.userId
125                                 )
126                             )
127                         }
128 
129                         override fun onAuthenticationHelp(authInfo: AuthenticationHelpInfo) {
130                             updateAuthenticationState(
131                                 AuthenticationState.Help(
132                                     authInfo.biometricSourceType,
133                                     authInfo.helpString,
134                                     authInfo.helpCode,
135                                     authInfo.requestReason.toAuthenticationReason()
136                                 )
137                             )
138                         }
139 
140                         override fun onAuthenticationStarted(authInfo: AuthenticationStartedInfo) {
141                             updateAuthenticationState(
142                                 AuthenticationState.Started(
143                                     authInfo.biometricSourceType,
144                                     authInfo.requestReason.toAuthenticationReason()
145                                 )
146                             )
147                         }
148 
149                         override fun onAuthenticationStopped(authInfo: AuthenticationStoppedInfo) {
150                             updateAuthenticationState(
151                                 AuthenticationState.Stopped(
152                                     authInfo.biometricSourceType,
153                                     AuthenticationReason.NotRunning
154                                 )
155                             )
156                         }
157 
158                         override fun onAuthenticationSucceeded(
159                             authInfo: AuthenticationSucceededInfo
160                         ) {
161                             updateAuthenticationState(
162                                 AuthenticationState.Succeeded(
163                                     authInfo.biometricSourceType,
164                                     authInfo.isIsStrongBiometric,
165                                     authInfo.requestReason.toAuthenticationReason(),
166                                     authInfo.userId
167                                 )
168                             )
169                         }
170                     }
171 
172                 updateAuthenticationState(
173                     AuthenticationState.Idle(requestReason = AuthenticationReason.NotRunning)
174                 )
175                 biometricManager?.registerAuthenticationStateListener(authenticationStateListener)
176                 awaitClose {
177                     biometricManager?.unregisterAuthenticationStateListener(
178                         authenticationStateListener
179                     )
180                 }
181             }
182             .distinctUntilChanged()
183             .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
184 
185     private val fingerprintAuthenticationState: Flow<AuthenticationState> =
186         authenticationState
<lambda>null187             .filter {
188                 it.biometricSourceType == null ||
189                     it.biometricSourceType == BiometricSourceType.FINGERPRINT
190             }
<lambda>null191             .onEach { Log.d(TAG, "fingerprintAuthenticationState updated: $it") }
192 
193     private val fingerprintRunningState: Flow<AuthenticationState> =
194         fingerprintAuthenticationState
<lambda>null195             .filter {
196                 it is AuthenticationState.Idle ||
197                     it is AuthenticationState.Started ||
198                     it is AuthenticationState.Stopped
199             }
<lambda>null200             .onEach { Log.d(TAG, "fingerprintRunningState updated: $it") }
201 
202     override val fingerprintAuthenticationReason: Flow<AuthenticationReason> =
203         fingerprintRunningState
<lambda>null204             .map { it.requestReason }
<lambda>null205             .onEach { Log.d(TAG, "fingerprintAuthenticationReason updated: $it") }
206 
207     override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
<lambda>null208         fingerprintAuthenticationState.filterIsInstance<AuthenticationState.Acquired>().map {
209             AcquiredFingerprintAuthenticationStatus(it.requestReason, it.acquiredInfo)
210         }
211 
212     companion object {
213         private const val TAG = "BiometricStatusRepositoryImpl"
214     }
215 }
216 
toAuthenticationReasonnull217 private fun Int.toAuthenticationReason(): AuthenticationReason =
218     when (this) {
219         REASON_AUTH_BP -> AuthenticationReason.BiometricPromptAuthentication
220         REASON_AUTH_KEYGUARD -> AuthenticationReason.DeviceEntryAuthentication
221         REASON_AUTH_OTHER -> AuthenticationReason.OtherAuthentication
222         REASON_AUTH_SETTINGS ->
223             AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER)
224         REASON_ENROLL_ENROLLING ->
225             AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING)
226         REASON_ENROLL_FIND_SENSOR ->
227             AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_FIND_SENSOR)
228         else -> AuthenticationReason.Unknown
229     }
230