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