1 /* 2 * 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 18 19 import android.app.IActivityTaskManager 20 import android.os.RemoteException 21 import android.util.Log 22 import android.view.IRemoteAnimationFinishedCallback 23 import android.view.RemoteAnimationTarget 24 import android.view.WindowManager 25 import com.android.internal.widget.LockPatternUtils 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Main 28 import com.android.systemui.dagger.qualifiers.UiBackground 29 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor 30 import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor 31 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier 32 import com.android.systemui.statusbar.policy.KeyguardStateController 33 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 34 import com.android.window.flags.Flags 35 import com.android.wm.shell.keyguard.KeyguardTransitions 36 import java.util.concurrent.Executor 37 import javax.inject.Inject 38 39 /** 40 * Manages lockscreen and AOD visibility state via the [IActivityTaskManager], and keeps track of 41 * remote animations related to changes in lockscreen visibility. 42 */ 43 @SysUISingleton 44 class WindowManagerLockscreenVisibilityManager 45 @Inject 46 constructor( 47 @Main private val executor: Executor, 48 @UiBackground private val uiBgExecutor: Executor, 49 private val activityTaskManagerService: IActivityTaskManager, 50 private val keyguardStateController: KeyguardStateController, 51 private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier, 52 private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, 53 private val keyguardTransitions: KeyguardTransitions, 54 private val selectedUserInteractor: SelectedUserInteractor, 55 private val lockPatternUtils: LockPatternUtils, 56 private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor, 57 ) { 58 59 /** 60 * Whether the lockscreen is showing, which we pass to [IActivityTaskManager.setLockScreenShown] 61 * in order to show the lockscreen and hide the surface behind the keyguard (or the inverse). 62 * 63 * This value is null if we have not yet called setLockScreenShown with any value. This will 64 * happen during the boot sequence, but we can't default to true here since otherwise we'll 65 * short-circuit on the first call to setLockScreenShown since we'll think we're already 66 * showing. 67 */ 68 private var isLockscreenShowing: Boolean? = null 69 70 /** 71 * Whether AOD is showing, which we pass to [IActivityTaskManager.setLockScreenShown] in order 72 * to show AOD when the lockscreen is visible. 73 */ 74 private var isAodVisible = false 75 76 /** 77 * Whether the keyguard is currently "going away", which we triggered via a call to 78 * [IActivityTaskManager.keyguardGoingAway]. When we tell WM that the keyguard is going away, 79 * the app/launcher surface behind the keyguard is made visible, and WM calls 80 * [onKeyguardGoingAwayRemoteAnimationStart] with a RemoteAnimationTarget so that we can animate 81 * it. 82 * 83 * Going away does not inherently result in [isLockscreenShowing] being set to false; we need to 84 * do that ourselves once we are done animating the surface. 85 * 86 * THIS IS THE ONLY PLACE 'GOING AWAY' TERMINOLOGY SHOULD BE USED. 'Going away' is a WM concept 87 * and we have gotten into trouble using it to mean various different things in the past. Unlock 88 * animations may still be visible when the keyguard is NOT 'going away', for example, when we 89 * play in-window animations, we set the surface to alpha=1f and end the animation immediately. 90 * The remainder of the animation occurs in-window, so while you might expect that the keyguard 91 * is still 'going away' because unlock animations are playing, it's actually not. 92 * 93 * If you want to know if the keyguard is 'going away', you probably want to check if we have 94 * STARTED but not FINISHED a transition to GONE. 95 * 96 * The going away animation will run until: 97 * - We manually call [endKeyguardGoingAwayAnimation] after we're done animating. 98 * - We call [setLockscreenShown] = true, which cancels the going away animation. 99 * - WM calls [onKeyguardGoingAwayRemoteAnimationCancelled] for another reason (such as the 10 100 * second timeout). 101 */ 102 private var isKeyguardGoingAway = false 103 private set(goingAway) { 104 // TODO(b/278086361): Extricate the keyguard state controller. 105 keyguardStateController.notifyKeyguardGoingAway(goingAway) 106 107 if (goingAway) { 108 keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId() 109 } 110 111 field = goingAway 112 } 113 114 /** 115 * The current user ID when we asked WM to start the keyguard going away animation. This is used 116 * for validation when user switching occurs during unlock. 117 */ 118 private var keyguardGoingAwayRequestedForUserId: Int = -1 119 120 /** Callback provided by WM to call once we're done with the going away animation. */ 121 private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null 122 123 private val enableNewKeyguardShellTransitions: Boolean = 124 Flags.ensureKeyguardDoesTransitionStarting() 125 126 /** 127 * Set the visibility of the surface behind the keyguard, making the appropriate calls to Window 128 * Manager to effect the change. 129 */ setSurfaceBehindVisibilitynull130 fun setSurfaceBehindVisibility(visible: Boolean) { 131 if (isKeyguardGoingAway && visible) { 132 Log.d(TAG, "#setSurfaceBehindVisibility: already visible, ignoring") 133 return 134 } 135 136 // The surface behind is always visible if the lockscreen is not showing, so we're already 137 // visible. 138 if (visible && isLockscreenShowing != true) { 139 Log.d(TAG, "#setSurfaceBehindVisibility: ignoring since the lockscreen isn't showing") 140 return 141 } 142 143 if (visible) { 144 if (enableNewKeyguardShellTransitions) { 145 startKeyguardTransition(false, /* keyguardShowing */ false /* aodShowing */) 146 isKeyguardGoingAway = true 147 return 148 } 149 150 isKeyguardGoingAway = true 151 Log.d(TAG, "Enqueuing ATMS#keyguardGoingAway() on uiBgExecutor") 152 uiBgExecutor.execute { 153 // Make the surface behind the keyguard visible by calling keyguardGoingAway. The 154 // lockscreen is still showing as well, allowing us to animate unlocked. 155 Log.d(TAG, "ATMS#keyguardGoingAway()") 156 activityTaskManagerService.keyguardGoingAway(0) 157 } 158 } else if (isLockscreenShowing == true) { 159 // Re-show the lockscreen if the surface was visible and we want to make it invisible, 160 // and the lockscreen is currently showing (this is the usual case of the going away 161 // animation). Re-showing the lockscreen will cancel the going away animation. If we 162 // want to hide the surface, but the lockscreen is not currently showing, do nothing and 163 // wait for lockscreenVisibility to emit if it's appropriate to show the lockscreen (it 164 // might be disabled/suppressed). 165 Log.d( 166 TAG, 167 "setLockscreenShown(true) because we're setting the surface invisible " + 168 "and lockscreen is already showing.", 169 ) 170 setLockscreenShown(true) 171 } 172 } 173 setAodVisiblenull174 fun setAodVisible(aodVisible: Boolean) { 175 setWmLockscreenState(aodVisible = aodVisible) 176 } 177 178 /** Sets the visibility of the lockscreen. */ setLockscreenShownnull179 fun setLockscreenShown(lockscreenShown: Boolean) { 180 setWmLockscreenState(lockscreenShowing = lockscreenShown) 181 } 182 183 /** 184 * Called when the keyguard going away remote animation is started, and we have a 185 * RemoteAnimationTarget to animate. 186 * 187 * This is triggered either by this class calling ATMS#keyguardGoingAway, or by WM directly, 188 * such as when an activity with FLAG_DISMISS_KEYGUARD is launched over a dismissible keyguard. 189 */ onKeyguardGoingAwayRemoteAnimationStartnull190 fun onKeyguardGoingAwayRemoteAnimationStart( 191 @WindowManager.TransitionOldType transit: Int, 192 apps: Array<RemoteAnimationTarget>, 193 wallpapers: Array<RemoteAnimationTarget>, 194 nonApps: Array<RemoteAnimationTarget>, 195 finishedCallback: IRemoteAnimationFinishedCallback, 196 ) { 197 goingAwayRemoteAnimationFinishedCallback = finishedCallback 198 199 if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) { 200 Log.d(TAG, "User switched during keyguard going away - ending remote animation.") 201 endKeyguardGoingAwayAnimation() 202 return 203 } 204 205 // If we weren't expecting the keyguard to be going away, WM triggered this transition. 206 if (!isKeyguardGoingAway) { 207 // Since WM triggered this, we're likely not transitioning to GONE yet. See if we can 208 // start that transition. 209 keyguardDismissTransitionInteractor.startDismissKeyguardTransition( 210 reason = "Going away remote animation started", 211 onAlreadyGone = { 212 // Called if we're already GONE by the time the dismiss transition would have 213 // started. This can happen due to timing issues, where the remote animation 214 // took a long time to start, and something else caused us to unlock in the 215 // meantime. Since we're already GONE, simply end the remote animation 216 // immediately. 217 Log.d( 218 TAG, 219 "onKeyguardGoingAwayRemoteAnimationStart: " + 220 "Dismiss transition was not started; we're already GONE. " + 221 "Ending remote animation.", 222 ) 223 finishedCallback.onAnimationFinished() 224 isKeyguardGoingAway = false 225 }, 226 ) 227 228 isKeyguardGoingAway = true 229 } 230 231 if (apps.isNotEmpty()) { 232 keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) 233 } else { 234 // Nothing to do here if we have no apps, end the animation, which will cancel it and WM 235 // will make *something* visible. 236 finishedCallback.onAnimationFinished() 237 } 238 } 239 onKeyguardGoingAwayRemoteAnimationCancellednull240 fun onKeyguardGoingAwayRemoteAnimationCancelled() { 241 // If WM cancelled the animation, we need to end immediately even if we're still using the 242 // animation. 243 endKeyguardGoingAwayAnimation() 244 maybeStartTransitionIfUserSwitchedDuringGoingAway() 245 } 246 247 /** 248 * Whether the going away remote animation target is in-use, which means we're animating it or 249 * intend to animate it. 250 * 251 * Some unlock animations (such as the translation spring animation) are non-deterministic and 252 * might end after the transition to GONE ends. In that case, we want to keep the remote 253 * animation running until the spring ends. 254 */ setUsingGoingAwayRemoteAnimationnull255 fun setUsingGoingAwayRemoteAnimation(usingTarget: Boolean) { 256 if (!usingTarget) { 257 endKeyguardGoingAwayAnimation() 258 } 259 } 260 261 /** 262 * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown. 263 * 264 * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This 265 * will be decided by the [KeyguardTransitionBootInteractor] shortly. 266 */ setWmLockscreenStatenull267 private fun setWmLockscreenState( 268 lockscreenShowing: Boolean? = this.isLockscreenShowing, 269 aodVisible: Boolean = this.isAodVisible, 270 ) { 271 if (lockscreenShowing == null) { 272 Log.d( 273 TAG, 274 "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" + 275 "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" + 276 "will happen once KeyguardTransitionBootInteractor starts the boot transition.", 277 ) 278 this.isAodVisible = aodVisible 279 return 280 } 281 282 if ( 283 this.isLockscreenShowing == lockscreenShowing && 284 this.isAodVisible == aodVisible && 285 !this.isKeyguardGoingAway 286 ) { 287 Log.d( 288 TAG, 289 "#setWmLockscreenState: lockscreenShowing=$lockscreenShowing and " + 290 "isAodVisible=$aodVisible were both unchanged and we're not going away, not " + 291 "forwarding to ATMS.", 292 ) 293 return 294 } 295 296 this.isLockscreenShowing = lockscreenShowing 297 this.isAodVisible = aodVisible 298 Log.d( 299 TAG, 300 "Enqueuing ATMS#setLockScreenShown($lockscreenShowing, $aodVisible) " + 301 "on uiBgExecutor", 302 ) 303 uiBgExecutor.execute { 304 Log.d( 305 TAG, 306 "ATMS#setLockScreenShown(" + 307 "isLockscreenShowing=$lockscreenShowing, " + 308 "aodVisible=$aodVisible).", 309 ) 310 if (enableNewKeyguardShellTransitions) { 311 startKeyguardTransition(lockscreenShowing, aodVisible) 312 } else { 313 try { 314 activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible) 315 } catch (e: RemoteException) { 316 Log.e(TAG, "Remote exception", e) 317 } 318 } 319 } 320 } 321 startKeyguardTransitionnull322 private fun startKeyguardTransition(keyguardShowing: Boolean, aodShowing: Boolean) { 323 keyguardTransitions.startKeyguardTransition(keyguardShowing, aodShowing) 324 } 325 endKeyguardGoingAwayAnimationnull326 private fun endKeyguardGoingAwayAnimation() { 327 if (!isKeyguardGoingAway) { 328 Log.d( 329 TAG, 330 "#endKeyguardGoingAwayAnimation() called when isKeyguardGoingAway=false. " + 331 "Short-circuiting.", 332 ) 333 return 334 } 335 336 executor.execute { 337 Log.d(TAG, "Finishing remote animation.") 338 goingAwayRemoteAnimationFinishedCallback?.onAnimationFinished() 339 goingAwayRemoteAnimationFinishedCallback = null 340 341 isKeyguardGoingAway = false 342 343 keyguardSurfaceBehindAnimator.notifySurfaceReleased() 344 } 345 } 346 347 /** 348 * If necessary, start a transition to show/hide keyguard in response to a user switch during 349 * keyguard going away. 350 * 351 * Returns [true] if a transition was started, or false if a transition was not necessary. 352 */ maybeStartTransitionIfUserSwitchedDuringGoingAwaynull353 private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean { 354 val currentUser = selectedUserInteractor.getSelectedUserId() 355 if (currentUser != keyguardGoingAwayRequestedForUserId) { 356 if (lockPatternUtils.isSecure(currentUser)) { 357 keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway() 358 } else { 359 keyguardDismissTransitionInteractor.startDismissKeyguardTransition( 360 reason = "User switch during keyguard going away, and new user is insecure" 361 ) 362 } 363 364 return true 365 } else { 366 return false 367 } 368 } 369 370 companion object { 371 private val TAG = "WindowManagerLsVis" 372 } 373 } 374