• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.deviceentry.domain.interactor
18 
19 import androidx.annotation.VisibleForTesting
20 import com.android.keyguard.KeyguardUpdateMonitor
21 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
22 import com.android.systemui.biometrics.AuthController
23 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
26 import com.android.systemui.dump.DumpManager
27 import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor
28 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
29 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
30 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
31 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
32 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
33 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
34 import com.android.systemui.scene.domain.interactor.SceneInteractor
35 import com.android.systemui.scene.shared.flag.SceneContainerFlag
36 import com.android.systemui.scene.shared.model.Overlays
37 import com.android.systemui.scene.shared.model.Scenes
38 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
39 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
40 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_ONLY_WAKE
41 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER
42 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING
43 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
44 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM
45 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
46 import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
47 import com.android.systemui.statusbar.phone.DozeScrimController
48 import com.android.systemui.util.kotlin.FlowDumperImpl
49 import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
50 import com.android.systemui.util.kotlin.combine
51 import com.android.systemui.util.kotlin.sample
52 import javax.inject.Inject
53 import kotlinx.coroutines.flow.Flow
54 import kotlinx.coroutines.flow.MutableSharedFlow
55 import kotlinx.coroutines.flow.combine
56 import kotlinx.coroutines.flow.distinctUntilChanged
57 import kotlinx.coroutines.flow.filter
58 import kotlinx.coroutines.flow.filterIsInstance
59 import kotlinx.coroutines.flow.filterNotNull
60 import kotlinx.coroutines.flow.map
61 import kotlinx.coroutines.flow.merge
62 
63 /**
64  * Hosts application business logic related to the source of the user entering the device. Note: The
65  * source of the user entering the device isn't equivalent to the reason the device is unlocked.
66  *
67  * For example, the user successfully enters the device when they dismiss the lockscreen via a
68  * bypass biometric or, if the device is already unlocked, by triggering an affordance that
69  * dismisses the lockscreen.
70  */
71 @SysUISingleton
72 class DeviceEntrySourceInteractor
73 @Inject
74 constructor(
75     authenticationInteractor: AuthenticationInteractor,
76     authController: AuthController,
77     alternateBouncerInteractor: AlternateBouncerInteractor,
78     deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
79     deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
80     dozeScrimController: DozeScrimController,
81     keyguardBypassInteractor: KeyguardBypassInteractor,
82     keyguardUpdateMonitor: KeyguardUpdateMonitor,
83     keyguardInteractor: KeyguardInteractor,
84     sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
85     sceneInteractor: SceneInteractor,
86     dumpManager: DumpManager,
87 ) : FlowDumperImpl(dumpManager) {
88     private val isShowingBouncerOverlay: Flow<Boolean> =
89         sceneInteractor.transitionState
90             .map { transitionState ->
91                 transitionState.isIdle(overlay = Overlays.Bouncer) ||
92                     transitionState.isTransitioning(null, Overlays.Bouncer)
93             }
94             .distinctUntilChanged()
95             .dumpWhileCollecting("isShowingBouncerOverlay")
96 
97     private val isUnlockedWithStrongFaceUnlock =
98         deviceEntryFaceAuthInteractor.authenticationStatus
99             .map { status ->
100                 (status as? SuccessFaceAuthenticationStatus)?.successResult?.isStrongBiometric
101                     ?: false
102             }
103             .dumpWhileCollecting("unlockedWithStrongFaceUnlock")
104 
105     private val isUnlockedWithStrongFingerprintUnlock =
106         deviceEntryFingerprintAuthInteractor.authenticationStatus
107             .map { status ->
108                 (status as? SuccessFingerprintAuthenticationStatus)?.isStrongBiometric ?: false
109             }
110             .dumpWhileCollecting("unlockedWithStrongFingerprintUnlock")
111 
112     private val faceWakeAndUnlockMode: Flow<BiometricUnlockMode> =
113         combine(
114                 alternateBouncerInteractor.isVisible,
115                 keyguardBypassInteractor.isBypassAvailable,
116                 isUnlockedWithStrongFaceUnlock,
117                 sceneContainerOcclusionInteractor.isOccludingActivityShown,
118                 sceneInteractor.currentScene,
119                 isShowingBouncerOverlay,
120             ) {
121                 isAlternateBouncerVisible,
122                 isBypassAvailable,
123                 isFaceStrongBiometric,
124                 isOccluded,
125                 currentScene,
126                 isShowingBouncerOverlay ->
127                 val isUnlockingAllowed =
128                     keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isFaceStrongBiometric)
129                 val bypass = isBypassAvailable || authController.isUdfpsFingerDown()
130 
131                 when {
132                     !keyguardUpdateMonitor.isDeviceInteractive ->
133                         when {
134                             !isUnlockingAllowed -> if (bypass) MODE_SHOW_BOUNCER else MODE_NONE
135                             bypass -> MODE_WAKE_AND_UNLOCK_PULSING
136                             else -> MODE_ONLY_WAKE
137                         }
138 
139                     isUnlockingAllowed && currentScene == Scenes.Dream ->
140                         if (bypass) MODE_WAKE_AND_UNLOCK_FROM_DREAM else MODE_ONLY_WAKE
141 
142                     isUnlockingAllowed && isOccluded -> MODE_UNLOCK_COLLAPSING
143 
144                     (isShowingBouncerOverlay || isAlternateBouncerVisible) && isUnlockingAllowed ->
145                         MODE_DISMISS_BOUNCER
146 
147                     isUnlockingAllowed && bypass -> MODE_UNLOCK_COLLAPSING
148 
149                     bypass -> MODE_SHOW_BOUNCER
150 
151                     else -> MODE_NONE
152                 }
153             }
154             .map { biometricModeIntToObject(it) }
155             .dumpWhileCollecting("faceWakeAndUnlockMode")
156 
157     private val fingerprintWakeAndUnlockMode: Flow<BiometricUnlockMode> =
158         combine(
159                 alternateBouncerInteractor.isVisible,
160                 authenticationInteractor.authenticationMethod,
161                 sceneInteractor.currentScene,
162                 sceneInteractor.currentOverlays,
163                 isUnlockedWithStrongFingerprintUnlock,
164                 isShowingBouncerOverlay,
165             ) {
166                 alternateBouncerVisible,
167                 authenticationMethod,
168                 currentScene,
169                 currentOverlays,
170                 isFingerprintStrongBiometric,
171                 isShowingBouncerOverlay ->
172                 val unlockingAllowed =
173                     keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
174                         isFingerprintStrongBiometric
175                     )
176                 when {
177                     !keyguardUpdateMonitor.isDeviceInteractive ->
178                         when {
179                             dozeScrimController.isPulsing && unlockingAllowed ->
180                                 MODE_WAKE_AND_UNLOCK_PULSING
181 
182                             unlockingAllowed || !authenticationMethod.isSecure ->
183                                 MODE_WAKE_AND_UNLOCK
184 
185                             else -> MODE_SHOW_BOUNCER
186                         }
187 
188                     unlockingAllowed && currentScene == Scenes.Dream ->
189                         MODE_WAKE_AND_UNLOCK_FROM_DREAM
190 
191                     isShowingBouncerOverlay && unlockingAllowed -> MODE_DISMISS_BOUNCER
192 
193                     unlockingAllowed -> MODE_UNLOCK_COLLAPSING
194 
195                     Overlays.Bouncer !in currentOverlays && !alternateBouncerVisible ->
196                         MODE_SHOW_BOUNCER
197 
198                     else -> MODE_NONE
199                 }
200             }
201             .map { biometricModeIntToObject(it) }
202             .dumpWhileCollecting("fingerprintWakeAndUnlockMode")
203 
204     @VisibleForTesting
205     private val biometricUnlockStateOnKeyguardDismissed =
206         merge(
207                 fingerprintWakeAndUnlockMode
208                     .filter { BiometricUnlockMode.dismissesKeyguard(it) }
209                     .map { mode ->
210                         BiometricUnlockModel(mode, BiometricUnlockSource.FINGERPRINT_SENSOR)
211                     },
212                 faceWakeAndUnlockMode
213                     .filter { BiometricUnlockMode.dismissesKeyguard(it) }
214                     .map { mode -> BiometricUnlockModel(mode, BiometricUnlockSource.FACE_SENSOR) },
215             )
216             .dumpWhileCollecting("biometricUnlockState")
217 
218     private val deviceEntryFingerprintAuthWakeAndUnlockEvents:
219         Flow<SuccessFingerprintAuthenticationStatus> =
220         deviceEntryFingerprintAuthInteractor.authenticationStatus
221             .filterIsInstance<SuccessFingerprintAuthenticationStatus>()
222             .dumpWhileCollecting("deviceEntryFingerprintAuthSuccessEvents")
223 
224     private val deviceEntryFaceAuthWakeAndUnlockEvents: Flow<SuccessFaceAuthenticationStatus> =
225         deviceEntryFaceAuthInteractor.authenticationStatus
226             .filterIsInstance<SuccessFaceAuthenticationStatus>()
227             .sampleFilter(
228                 combine(
229                     sceneContainerOcclusionInteractor.isOccludingActivityShown,
230                     keyguardBypassInteractor.isBypassAvailable,
231                     keyguardBypassInteractor.canBypass,
232                     ::Triple,
233                 )
234             ) { (isOccludingActivityShown, isBypassAvailable, canBypass) ->
235                 isOccludingActivityShown || !isBypassAvailable || canBypass
236             }
237             .dumpWhileCollecting("deviceEntryFaceAuthSuccessEvents")
238 
239     private val deviceEntryBiometricAuthSuccessEvents =
240         merge(deviceEntryFingerprintAuthWakeAndUnlockEvents, deviceEntryFaceAuthWakeAndUnlockEvents)
241             .dumpWhileCollecting("deviceEntryBiometricAuthSuccessEvents")
242 
243     val deviceEntryFromBiometricSource: Flow<BiometricUnlockSource> =
244         if (SceneContainerFlag.isEnabled) {
245                 deviceEntryBiometricAuthSuccessEvents
246                     .sample(biometricUnlockStateOnKeyguardDismissed)
247                     .map { it.source }
248                     .filterNotNull()
249             } else {
250                 keyguardInteractor.biometricUnlockState
251                     .filter { BiometricUnlockMode.dismissesKeyguard(it.mode) }
252                     .map { it.source }
253                     .filterNotNull()
254             }
255             .dumpWhileCollecting("deviceEntryFromBiometricSource")
256 
257     private val _attemptEnterDeviceFromDeviceEntryIcon: MutableSharedFlow<Unit> =
258         MutableSharedFlow()
259     val attemptEnterDeviceFromDeviceEntryIcon = _attemptEnterDeviceFromDeviceEntryIcon
260 
261     suspend fun attemptEnterDeviceFromDeviceEntryIcon() {
262         _attemptEnterDeviceFromDeviceEntryIcon.emit(Unit)
263     }
264 
265     private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockMode {
266         return when (value) {
267             MODE_NONE -> BiometricUnlockMode.NONE
268             MODE_WAKE_AND_UNLOCK -> BiometricUnlockMode.WAKE_AND_UNLOCK
269             MODE_WAKE_AND_UNLOCK_PULSING -> BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING
270             MODE_SHOW_BOUNCER -> BiometricUnlockMode.SHOW_BOUNCER
271             MODE_ONLY_WAKE -> BiometricUnlockMode.ONLY_WAKE
272             MODE_UNLOCK_COLLAPSING -> BiometricUnlockMode.UNLOCK_COLLAPSING
273             MODE_WAKE_AND_UNLOCK_FROM_DREAM -> BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM
274             MODE_DISMISS_BOUNCER -> BiometricUnlockMode.DISMISS_BOUNCER
275             else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
276         }
277     }
278 }
279