1 /*
<lambda>null2  * Copyright (C) 2024 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.deviceentry.domain.interactor
18 
19 import android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START
20 import android.hardware.face.FaceManager
21 import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Application
24 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
25 import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
26 import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.ExperimentalCoroutinesApi
30 import kotlinx.coroutines.flow.Flow
31 import kotlinx.coroutines.flow.emptyFlow
32 import kotlinx.coroutines.flow.filterIsInstance
33 import kotlinx.coroutines.flow.flatMapLatest
34 import kotlinx.coroutines.launch
35 
36 /**
37  * FaceHelpMessageDeferral business logic. Processes face acquired and face help authentication
38  * events to determine whether a face auth event should be displayed to the user immediately or when
39  * a [FaceManager.FACE_ERROR_TIMEOUT] is received.
40  */
41 @ExperimentalCoroutinesApi
42 @SysUISingleton
43 class FaceHelpMessageDeferralInteractor
44 @Inject
45 constructor(
46     @Application private val scope: CoroutineScope,
47     faceAuthInteractor: DeviceEntryFaceAuthInteractor,
48     private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
49     faceHelpMessageDeferralFactory: FaceHelpMessageDeferralFactory,
50 ) {
51     private val faceHelpMessageDeferral = faceHelpMessageDeferralFactory.create()
52     private val faceAcquired: Flow<AcquiredFaceAuthenticationStatus> =
53         faceAuthInteractor.authenticationStatus.filterIsInstance<AcquiredFaceAuthenticationStatus>()
54     private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
55         faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
56 
57     init {
58         if (DeviceEntryUdfpsRefactor.isEnabled) {
59             startUpdatingFaceHelpMessageDeferral()
60         }
61     }
62 
63     /**
64      * If the given [HelpFaceAuthenticationStatus] msgId should be deferred to
65      * [FaceManager.FACE_ERROR_TIMEOUT].
66      */
67     fun shouldDefer(msgId: Int): Boolean {
68         return faceHelpMessageDeferral.shouldDefer(msgId)
69     }
70 
71     /**
72      * Message that was deferred to show at [FaceManager.FACE_ERROR_TIMEOUT], if any. Returns null
73      * if there are currently no valid deferred messages.
74      */
75     fun getDeferredMessage(): CharSequence? {
76         return faceHelpMessageDeferral.getDeferredMessage()
77     }
78 
79     private fun startUpdatingFaceHelpMessageDeferral() {
80         scope.launch {
81             biometricSettingsInteractor.isFaceAuthEnrolledAndEnabled
82                 .flatMapLatest { faceEnrolledAndEnabled ->
83                     if (faceEnrolledAndEnabled) {
84                         faceAcquired
85                     } else {
86                         emptyFlow()
87                     }
88                 }
89                 .collect {
90                     if (it.acquiredInfo == FACE_ACQUIRED_START) {
91                         faceHelpMessageDeferral.reset()
92                     }
93                     faceHelpMessageDeferral.processFrame(it.acquiredInfo)
94                 }
95         }
96 
97         scope.launch {
98             biometricSettingsInteractor.isFaceAuthEnrolledAndEnabled
99                 .flatMapLatest { faceEnrolledAndEnabled ->
100                     if (faceEnrolledAndEnabled) {
101                         faceHelp
102                     } else {
103                         emptyFlow()
104                     }
105                 }
106                 .collect { helpAuthenticationStatus ->
107                     helpAuthenticationStatus.msg?.let { msg ->
108                         faceHelpMessageDeferral.updateMessage(helpAuthenticationStatus.msgId, msg)
109                     }
110                 }
111         }
112     }
113 }
114