• 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 android.annotation.SuppressLint
20 import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
21 import android.os.Bundle
22 import android.os.IRemoteCallback
23 import android.os.RemoteException
24 import android.util.Log
25 import com.android.systemui.CoreStartable
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Background
28 import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
29 import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
30 import com.android.systemui.settings.UserTracker
31 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
32 import dagger.Lazy
33 import javax.inject.Inject
34 import kotlinx.coroutines.CoroutineScope
35 import kotlinx.coroutines.flow.MutableSharedFlow
36 import kotlinx.coroutines.launch
37 
38 /**
39  * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
40  * the device is awake and not going to sleep.
41  *
42  * This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
43  * because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
44  * screen is forced to stay awake but the lock timeout elapses.
45  *
46  * This is not the only way for the device to lock while the screen is on. The other cases, which do
47  * not directly involve [KeyguardService], are handled in [KeyguardShowWhileAwakeInteractor].
48  */
49 @SysUISingleton
50 class KeyguardServiceShowLockscreenInteractor
51 @Inject
52 constructor(
53     @Background val backgroundScope: CoroutineScope,
54     private val selectedUserInteractor: SelectedUserInteractor,
55     private val repository: KeyguardServiceShowLockscreenRepository,
56     private val userTracker: UserTracker,
57     private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
58     private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
59 ) : CoreStartable {
60 
61     override fun start() {
62         backgroundScope.launch {
63             // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
64             // This is not the only place we notify the lockNowCallbacks - there are cases where we
65             // decide not to show the lockscreen despite being asked to, and we need to notify the
66             // callback in those cases as well.
67             wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
68                 if (visible) {
69                     notifyShowLockscreenCallbacks()
70                 }
71             }
72         }
73     }
74 
75     /**
76      * Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
77      * right now, even though the device is awake and not going to sleep.
78      *
79      * WARNING: This is only one of multiple reasons the keyguard might need to show while not going
80      * to sleep. Unless you're dealing with keyguard internals that specifically need to know that
81      * we're locking due to a call to doKeyguardTimeout or showDismissibleKeyguard, use
82      * [KeyguardShowWhileAwakeInteractor.showWhileAwakeEvents].
83      *
84      * This is fundamentally an event flow, hence the SharedFlow.
85      */
86     @SuppressLint("SharedFlowCreation")
87     val showNowEvents: MutableSharedFlow<ShowWhileAwakeReason> = MutableSharedFlow()
88 
89     /**
90      * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
91      * the device locked while the screen was on.
92      *
93      * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
94      * later when we successfully show.
95      */
96     fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
97         backgroundScope.launch {
98             if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
99                 val userId = userTracker.userId
100 
101                 // This callback needs to be invoked after we show the lockscreen (or decide not to
102                 // show it) otherwise System UI will crash in 20 seconds, as a security measure.
103                 repository.addShowLockscreenCallback(
104                     userId,
105                     IRemoteCallback.Stub.asInterface(
106                         options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
107                     ),
108                 )
109 
110                 Log.d(
111                     TAG,
112                     "Showing lockscreen now - setting required callback for user $userId. " +
113                         "SysUI will crash if this callback is not invoked.",
114                 )
115 
116                 // If the keyguard is disabled or suppressed, we'll never actually show the
117                 // lockscreen. Notify the callback so we don't crash.
118                 if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
119                     Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
120                     notifyShowLockscreenCallbacks()
121                 }
122             }
123 
124             showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
125         }
126     }
127 
128     /**
129      * Called by [KeyguardService] when it receives a showDismissibleKeyguard() call. This indicates
130      * that the device was folded with settings configured to show a dismissible keyguard on the
131      * outer display.
132      */
133     fun onKeyguardServiceShowDismissibleKeyguard() {
134         backgroundScope.launch {
135             showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
136         }
137     }
138 
139     /** Notifies the callbacks that we've either locked, or decided not to lock. */
140     private fun notifyShowLockscreenCallbacks() {
141         var callbacks: MutableList<ShowLockscreenCallback>
142 
143         synchronized(repository.showLockscreenCallbacks) {
144             callbacks = ArrayList(repository.showLockscreenCallbacks)
145             repository.showLockscreenCallbacks.clear()
146         }
147 
148         callbacks.forEach { callback ->
149             if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
150                 Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
151                 return
152             }
153             Log.i(TAG, "Notifying lockNowCallback")
154             try {
155                 callback.remoteCallback.sendResult(null)
156             } catch (e: RemoteException) {
157                 Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
158             }
159         }
160     }
161 
162     companion object {
163         private const val TAG = "ShowLockscreenInteractor"
164     }
165 }
166