• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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