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