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.keyguard.domain.interactor 18 19 import com.android.app.tracing.coroutines.launchTraced as launch 20 import com.android.internal.policy.IKeyguardDismissCallback 21 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor 22 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Application 25 import com.android.systemui.dagger.qualifiers.Main 26 import com.android.systemui.keyguard.DismissCallbackRegistry 27 import com.android.systemui.keyguard.data.repository.KeyguardRepository 28 import com.android.systemui.keyguard.data.repository.TrustRepository 29 import com.android.systemui.keyguard.shared.model.DismissAction 30 import com.android.systemui.keyguard.shared.model.KeyguardDone 31 import com.android.systemui.power.domain.interactor.PowerInteractor 32 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 33 import com.android.systemui.util.kotlin.Utils.Companion.toQuad 34 import com.android.systemui.util.kotlin.sample 35 import javax.inject.Inject 36 import kotlinx.coroutines.CoroutineDispatcher 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.combine 40 import kotlinx.coroutines.flow.filter 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.merge 43 import kotlinx.coroutines.withContext 44 45 /** Encapsulates business logic for requesting the keyguard to dismiss/finish/done. */ 46 @SysUISingleton 47 class KeyguardDismissInteractor 48 @Inject 49 constructor( 50 @Main private val mainDispatcher: CoroutineDispatcher, 51 @Application private val scope: CoroutineScope, 52 private val keyguardRepository: KeyguardRepository, 53 private val primaryBouncerInteractor: PrimaryBouncerInteractor, 54 private val selectedUserInteractor: SelectedUserInteractor, 55 private val dismissCallbackRegistry: DismissCallbackRegistry, 56 private val alternateBouncerInteractor: AlternateBouncerInteractor, 57 trustRepository: TrustRepository, 58 powerInteractor: PowerInteractor, 59 ) { 60 /* 61 * Updates when a biometric has authenticated the device and is requesting to dismiss 62 * the keyguard. When true, a class 3 biometrics has authenticated. Else, a lower class 63 * biometric strength has authenticated and is requesting to dismiss the keyguard. 64 */ 65 private val biometricAuthenticatedRequestDismissKeyguard: Flow<Unit> = 66 primaryBouncerInteractor.keyguardAuthenticatedBiometrics.map {} // map to Unit 67 68 /* 69 * Updates when a trust change is requesting to dismiss the keyguard and is able to do so 70 * in the current device state. 71 */ 72 private val onTrustGrantedRequestDismissKeyguard: Flow<Unit> = 73 trustRepository.trustAgentRequestingToDismissKeyguard 74 .sample( 75 combine( 76 primaryBouncerInteractor.isShowing, 77 alternateBouncerInteractor.isVisible, 78 powerInteractor.isInteractive, 79 ::Triple, 80 ), 81 ::toQuad, 82 ) 83 .filter { (trustModel, primaryBouncerShowing, altBouncerShowing, interactive) -> 84 val bouncerShowing = primaryBouncerShowing || altBouncerShowing 85 (interactive || trustModel.flags.temporaryAndRenewable()) && 86 (bouncerShowing || trustModel.flags.dismissKeyguardRequested()) 87 } 88 .map {} // map to Unit 89 90 /* 91 * Updates when the current user successfully has authenticated via primary authentication 92 * (pin/pattern/password). 93 */ 94 private val primaryAuthenticated: Flow<Unit> = 95 primaryBouncerInteractor.keyguardAuthenticatedPrimaryAuth 96 .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() } 97 .map {} // map to Unit 98 99 /* 100 * Updates when the current user requests the bouncer after they've already successfully 101 * authenticated (ie: from non-bypass face auth, from a trust agent that didn't immediately 102 * dismiss the keyguard, or if keyguard security is set to SWIPE or NONE). 103 */ 104 private val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Unit> = 105 primaryBouncerInteractor.userRequestedBouncerWhenAlreadyAuthenticated 106 .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() } 107 .map {} // map to Unit 108 109 /** Updates when keyguardDone should be requested. */ 110 val keyguardDone: Flow<KeyguardDone> = keyguardRepository.keyguardDone 111 112 /** Updates when any request to dismiss the current user's keyguard has arrived. */ 113 private val dismissKeyguardRequest: Flow<DismissAction> = 114 merge( 115 biometricAuthenticatedRequestDismissKeyguard, 116 onTrustGrantedRequestDismissKeyguard, 117 primaryAuthenticated, 118 userRequestedBouncerWhenAlreadyAuthenticated, 119 ) 120 .sample(keyguardRepository.dismissAction) 121 122 /** 123 * Updates when a request to dismiss the current user's keyguard has arrived and there's a 124 * dismiss action to run immediately. It's expected that the consumer will request keyguardDone 125 * with or without a deferral. 126 */ 127 val dismissKeyguardRequestWithImmediateDismissAction: Flow<Unit> = 128 dismissKeyguardRequest.filter { it is DismissAction.RunImmediately }.map {} // map to Unit 129 130 /** 131 * Updates when a request to dismiss the current user's keyguard has arrived and there's isn't a 132 * dismiss action to run immediately. There may still be a dismiss action to run after the 133 * keyguard transitions to GONE. 134 */ 135 val dismissKeyguardRequestWithoutImmediateDismissAction: Flow<Unit> = 136 dismissKeyguardRequest.filter { it !is DismissAction.RunImmediately }.map {} // map to Unit 137 138 suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) { 139 keyguardRepository.setKeyguardDone(keyguardDoneTiming) 140 } 141 142 /** 143 * Dismiss the keyguard (or show the bouncer) and invoke the provided callback once dismissed. 144 * 145 * TODO(b/358412565): Support dismiss messages. 146 */ 147 fun dismissKeyguardWithCallback(callback: IKeyguardDismissCallback?) { 148 scope.launch { 149 withContext(mainDispatcher) { 150 if (callback != null) { 151 dismissCallbackRegistry.addCallback(callback) 152 } 153 154 // This will either show the bouncer, or dismiss the keyguard if insecure. We 155 // currently need to request showing the bouncer in order to start a transition to 156 // *_BOUNCER. Once we refactor that so that starting the transition is what causes 157 // the bouncer to show, we can remove this entire method, and simply call 158 // KeyguardDismissTransitionInteractor#startDismissKeyguardTransition. 159 if (alternateBouncerInteractor.canShowAlternateBouncer.value) { 160 alternateBouncerInteractor.forceShow() 161 } else { 162 primaryBouncerInteractor.show( 163 true, 164 "KeyguardDismissInteractor#dismissKeyguardWithCallback", 165 ) 166 } 167 } 168 } 169 } 170 } 171