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.bouncer.data.repository
18
19 import android.hardware.biometrics.BiometricSourceType
20 import android.hardware.biometrics.BiometricSourceType.FACE
21 import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
22 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
23 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
24 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
25 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
26 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
27 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
28 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
29 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
30 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
31 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
32 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
33 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
34 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
35 import com.android.keyguard.KeyguardUpdateMonitor
36 import com.android.keyguard.KeyguardUpdateMonitorCallback
37 import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
38 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
39 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
40 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
41 import com.android.systemui.dagger.SysUISingleton
42 import com.android.systemui.flags.SystemPropertiesHelper
43 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
44 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
45 import com.android.systemui.keyguard.data.repository.TrustRepository
46 import com.android.systemui.user.data.repository.UserRepository
47 import javax.inject.Inject
48 import kotlinx.coroutines.channels.awaitClose
49 import kotlinx.coroutines.flow.Flow
50 import kotlinx.coroutines.flow.MutableStateFlow
51 import kotlinx.coroutines.flow.combine
52 import kotlinx.coroutines.flow.distinctUntilChanged
53 import kotlinx.coroutines.flow.map
54 import kotlinx.coroutines.flow.onStart
55
56 /** Provide different sources of messages that needs to be shown on the bouncer. */
57 interface BouncerMessageRepository {
58 /**
59 * Messages that are shown in response to the incorrect security attempts on the bouncer and
60 * primary authentication method being locked out, along with countdown messages before primary
61 * auth is active again.
62 */
63 val primaryAuthMessage: Flow<BouncerMessageModel?>
64
65 /**
66 * Help messages that are shown to the user on how to successfully perform authentication using
67 * face.
68 */
69 val faceAcquisitionMessage: Flow<BouncerMessageModel?>
70
71 /**
72 * Help messages that are shown to the user on how to successfully perform authentication using
73 * fingerprint.
74 */
75 val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?>
76
77 /** Custom message that is displayed when the bouncer is being shown to launch an app. */
78 val customMessage: Flow<BouncerMessageModel?>
79
80 /**
81 * Messages that are shown in response to biometric authentication attempts through face or
82 * fingerprint.
83 */
84 val biometricAuthMessage: Flow<BouncerMessageModel?>
85
86 /** Messages that are shown when certain auth flags are set. */
87 val authFlagsMessage: Flow<BouncerMessageModel?>
88
89 /** Messages that are show after biometrics are locked out temporarily or permanently */
90 val biometricLockedOutMessage: Flow<BouncerMessageModel?>
91
92 /** Set the value for [primaryAuthMessage] */
93 fun setPrimaryAuthMessage(value: BouncerMessageModel?)
94
95 /** Set the value for [faceAcquisitionMessage] */
96 fun setFaceAcquisitionMessage(value: BouncerMessageModel?)
97 /** Set the value for [fingerprintAcquisitionMessage] */
98 fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?)
99
100 /** Set the value for [customMessage] */
101 fun setCustomMessage(value: BouncerMessageModel?)
102
103 /**
104 * Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage],
105 * [fingerprintAcquisitionMessage] & [customMessage]
106 */
107 fun clearMessage()
108 }
109
110 private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
111 private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
112
113 @SysUISingleton
114 class BouncerMessageRepositoryImpl
115 @Inject
116 constructor(
117 trustRepository: TrustRepository,
118 biometricSettingsRepository: BiometricSettingsRepository,
119 updateMonitor: KeyguardUpdateMonitor,
120 private val bouncerMessageFactory: BouncerMessageFactory,
121 private val userRepository: UserRepository,
122 private val systemPropertiesHelper: SystemPropertiesHelper,
123 fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
124 ) : BouncerMessageRepository {
125
126 private val isAnyBiometricsEnabledAndEnrolled =
127 or(
128 biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
129 biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
130 )
131
132 private val wasRebootedForMainlineUpdate
133 get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
134
135 private val authFlagsBasedPromptReason: Flow<Int> =
136 combine(
137 biometricSettingsRepository.authenticationFlags,
138 trustRepository.isCurrentUserTrustManaged,
139 isAnyBiometricsEnabledAndEnrolled,
140 ::Triple
141 )
flagsnull142 .map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) ->
143 val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled)
144 return@map if (
145 trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
146 ) {
147 if (wasRebootedForMainlineUpdate) PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
148 else PROMPT_REASON_RESTART
149 } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
150 PROMPT_REASON_TIMEOUT
151 } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
152 PROMPT_REASON_DEVICE_ADMIN
153 } else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) {
154 PROMPT_REASON_TRUSTAGENT_EXPIRED
155 } else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
156 PROMPT_REASON_TRUSTAGENT_EXPIRED
157 } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
158 PROMPT_REASON_USER_REQUEST
159 } else if (
160 trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
161 ) {
162 PROMPT_REASON_PREPARE_FOR_UPDATE
163 } else if (
164 trustOrBiometricsAvailable &&
165 flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
166 ) {
167 PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
168 } else {
169 PROMPT_REASON_NONE
170 }
171 }
172
173 private val biometricAuthReason: Flow<Int> =
<lambda>null174 conflatedCallbackFlow {
175 val callback =
176 object : KeyguardUpdateMonitorCallback() {
177 override fun onBiometricAuthFailed(
178 biometricSourceType: BiometricSourceType?
179 ) {
180 val promptReason =
181 if (biometricSourceType == FINGERPRINT)
182 PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
183 else if (
184 biometricSourceType == FACE && !updateMonitor.isFaceLockedOut
185 ) {
186 PROMPT_REASON_INCORRECT_FACE_INPUT
187 } else PROMPT_REASON_NONE
188 trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed")
189 }
190
191 override fun onBiometricsCleared() {
192 trySendWithFailureLogging(
193 PROMPT_REASON_NONE,
194 TAG,
195 "onBiometricsCleared"
196 )
197 }
198
199 override fun onBiometricAcquired(
200 biometricSourceType: BiometricSourceType?,
201 acquireInfo: Int
202 ) {
203 trySendWithFailureLogging(
204 PROMPT_REASON_NONE,
205 TAG,
206 "clearBiometricPrompt for new auth session."
207 )
208 }
209
210 override fun onBiometricAuthenticated(
211 userId: Int,
212 biometricSourceType: BiometricSourceType?,
213 isStrongBiometric: Boolean
214 ) {
215 trySendWithFailureLogging(
216 PROMPT_REASON_NONE,
217 TAG,
218 "onBiometricAuthenticated"
219 )
220 }
221 }
222 updateMonitor.registerCallback(callback)
223 awaitClose { updateMonitor.removeCallback(callback) }
224 }
225 .distinctUntilChanged()
226
227 private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
228 override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage
229
230 private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
231 override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage
232
233 private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
234 override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> =
235 _fingerprintAcquisitionMessage
236
237 private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
238 override val customMessage: Flow<BouncerMessageModel?> = _customMessage
239
240 override val biometricAuthMessage: Flow<BouncerMessageModel?> =
241 biometricAuthReason
<lambda>null242 .map {
243 if (it == PROMPT_REASON_NONE) null
244 else
245 bouncerMessageFactory.createFromPromptReason(
246 it,
247 userRepository.getSelectedUserInfo().id
248 )
249 }
<lambda>null250 .onStart { emit(null) }
251 .distinctUntilChanged()
252
253 override val authFlagsMessage: Flow<BouncerMessageModel?> =
254 authFlagsBasedPromptReason
<lambda>null255 .map {
256 if (it == PROMPT_REASON_NONE) null
257 else
258 bouncerMessageFactory.createFromPromptReason(
259 it,
260 userRepository.getSelectedUserInfo().id
261 )
262 }
<lambda>null263 .onStart { emit(null) }
264 .distinctUntilChanged()
265
266 // TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system
267 // has been launched.
<lambda>null268 private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow {
269 val callback =
270 object : KeyguardUpdateMonitorCallback() {
271 override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
272 if (biometricSourceType == FACE) {
273 trySendWithFailureLogging(
274 updateMonitor.isFaceLockedOut,
275 TAG,
276 "face lock out state changed."
277 )
278 }
279 }
280 }
281 updateMonitor.registerCallback(callback)
282 trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value")
283 awaitClose { updateMonitor.removeCallback(callback) }
284 }
285
286 override val biometricLockedOutMessage: Flow<BouncerMessageModel?> =
facenull287 combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face ->
288 return@combine if (fp) {
289 bouncerMessageFactory.createFromPromptReason(
290 PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
291 userRepository.getSelectedUserInfo().id
292 )
293 } else if (face) {
294 bouncerMessageFactory.createFromPromptReason(
295 PROMPT_REASON_FACE_LOCKED_OUT,
296 userRepository.getSelectedUserInfo().id
297 )
298 } else null
299 }
300
setPrimaryAuthMessagenull301 override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
302 _primaryAuthMessage.value = value
303 }
304
setFaceAcquisitionMessagenull305 override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
306 _faceAcquisitionMessage.value = value
307 }
308
setFingerprintAcquisitionMessagenull309 override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
310 _fingerprintAcquisitionMessage.value = value
311 }
312
setCustomMessagenull313 override fun setCustomMessage(value: BouncerMessageModel?) {
314 _customMessage.value = value
315 }
316
clearMessagenull317 override fun clearMessage() {
318 _fingerprintAcquisitionMessage.value = null
319 _faceAcquisitionMessage.value = null
320 _primaryAuthMessage.value = null
321 _customMessage.value = null
322 }
323
324 companion object {
325 const val TAG = "BouncerDetailedMessageRepository"
326 }
327 }
328
ornull329 private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
330 flow.combine(anotherFlow) { a, b -> a || b }
331