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.keyguard.domain.interactor 18 19 import com.android.app.tracing.coroutines.launchTraced as launch 20 import com.android.internal.widget.LockPatternUtils 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository 25 import com.android.systemui.keyguard.data.repository.KeyguardRepository 26 import com.android.systemui.keyguard.shared.model.KeyguardState 27 import com.android.systemui.log.table.TableLogBuffer 28 import com.android.systemui.log.table.logDiffsForTable 29 import com.android.systemui.scene.shared.flag.SceneContainerFlag 30 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 31 import com.android.systemui.util.kotlin.sample 32 import javax.inject.Inject 33 import kotlinx.coroutines.CoroutineDispatcher 34 import kotlinx.coroutines.CoroutineScope 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.StateFlow 37 import kotlinx.coroutines.flow.collect 38 import kotlinx.coroutines.flow.filter 39 import kotlinx.coroutines.flow.map 40 import kotlinx.coroutines.flow.onEach 41 import kotlinx.coroutines.withContext 42 43 /** 44 * Logic around the keyguard being enabled, disabled, or suppressed via adb. If the keyguard is 45 * disabled or suppressed, the lockscreen cannot be shown and the device will go from AOD/DOZING 46 * directly to GONE. 47 * 48 * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold 49 * permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart 50 * rather than simply dismissing the keyguard or setting up the device to have Security: None, for 51 * reasons unknown. 52 * 53 * Keyguard can be suppressed by calling "adb shell locksettings set-disabled true", which is 54 * frequently done in tests. If keyguard is suppressed, it won't show even if the keyguard is 55 * enabled. If keyguard is not suppressed, then we defer to whether keyguard is enabled or disabled. 56 */ 57 @SysUISingleton 58 class KeyguardEnabledInteractor 59 @Inject 60 constructor( 61 @Application val scope: CoroutineScope, 62 @Background val backgroundDispatcher: CoroutineDispatcher, 63 val repository: KeyguardRepository, 64 val biometricSettingsRepository: BiometricSettingsRepository, 65 private val selectedUserInteractor: SelectedUserInteractor, 66 private val lockPatternUtils: LockPatternUtils, 67 keyguardDismissTransitionInteractor: dagger.Lazy<KeyguardDismissTransitionInteractor>, 68 internalTransitionInteractor: InternalKeyguardTransitionInteractor, 69 ) { 70 71 /** 72 * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the 73 * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE. 74 * 75 * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold 76 * permission to do so (such as Phone). 77 * 78 * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in 79 * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were 80 * locked when it was disabled. 81 * 82 * Even if the keyguard is enabled, it's possible for it to be suppressed temporarily via adb. 83 * If you need to respect that adb command, you will need to use 84 * [isKeyguardEnabledAndNotSuppressed] instead of using this flow. 85 */ 86 val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled 87 88 /** 89 * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it 90 * became disabled. 91 */ 92 val showKeyguardWhenReenabled: Flow<Boolean> = 93 repository.isKeyguardEnabled 94 .onEach { SceneContainerFlag.assertInLegacyMode() } 95 // Whenever the keyguard is disabled... 96 .filter { enabled -> !enabled } 97 .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair) 98 .map { (_, inLockdown) -> 99 val transitionInfo = internalTransitionInteractor.currentTransitionInfoInternal() 100 // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case, 101 // we want to remember that and re-show it when keyguard is enabled again. 102 transitionInfo.to != KeyguardState.GONE && !inLockdown 103 } 104 105 init { 106 /** 107 * Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already 108 * GONE. 109 */ 110 scope.launch { 111 if (!SceneContainerFlag.isEnabled) { 112 repository.isKeyguardEnabled 113 .filter { enabled -> !enabled } 114 .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair) 115 .collect { (_, inLockdown) -> 116 val currentTransitionInfo = 117 internalTransitionInteractor.currentTransitionInfoInternal() 118 if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) { 119 keyguardDismissTransitionInteractor 120 .get() 121 .startDismissKeyguardTransition("keyguard disabled") 122 } 123 } 124 } 125 } 126 } 127 128 fun notifyKeyguardEnabled(enabled: Boolean) { 129 repository.setKeyguardEnabled(enabled) 130 } 131 132 fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) { 133 repository.setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled) 134 } 135 136 fun isShowKeyguardWhenReenabled(): Boolean { 137 return repository.isShowKeyguardWhenReenabled() 138 } 139 140 /** 141 * Whether the keyguard is enabled, and has not been suppressed via adb. 142 * 143 * There is unfortunately no callback for [isKeyguardSuppressed], which means this can't be a 144 * flow, since it's ambiguous when we would query the latest suppression value. 145 */ 146 suspend fun isKeyguardEnabledAndNotSuppressed(): Boolean { 147 return isKeyguardEnabled.value && !isKeyguardSuppressed() 148 } 149 150 /** 151 * Returns whether the lockscreen has been disabled ("suppressed") via "adb shell locksettings 152 * set-disabled". If suppressed, we'll ignore all signals that would typically result in showing 153 * the keyguard, regardless of the value of [isKeyguardEnabled]. 154 * 155 * It's extremely confusing to have [isKeyguardEnabled] not be the inverse of "is lockscreen 156 * disabled", so this method intentionally re-terms it as "suppressed". 157 * 158 * Note that if the lockscreen is currently showing when it's suppressed, it will remain visible 159 * until it's unlocked, at which point it will never re-appear until suppression is removed. 160 */ 161 suspend fun isKeyguardSuppressed( 162 userId: Int = selectedUserInteractor.getSelectedUserId() 163 ): Boolean { 164 // isLockScreenDisabled returns true whenever keyguard is not enabled, even if the adb 165 // command was not used to disable/suppress the lockscreen. To make these booleans as clear 166 // as possible, only return true if keyguard is suppressed when it otherwise would have 167 // been enabled. 168 return withContext(backgroundDispatcher) { 169 isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId) 170 } 171 } 172 173 suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) { 174 isKeyguardEnabled 175 .logDiffsForTable( 176 tableLogBuffer = tableLogBuffer, 177 columnName = "isKeyguardEnabled", 178 initialValue = isKeyguardEnabled.value, 179 ) 180 .collect() 181 } 182 } 183