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