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