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.util.Log
21 import android.view.IRemoteAnimationFinishedCallback
22 import android.view.RemoteAnimationTarget
23 import android.view.WindowManager
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.dagger.qualifiers.Main
26 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
27 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
28 import com.android.systemui.statusbar.policy.KeyguardStateController
29 import java.util.concurrent.Executor
30 import javax.inject.Inject
31 
32 /**
33  * Manages lockscreen and AOD visibility state via the [IActivityTaskManager], and keeps track of
34  * remote animations related to changes in lockscreen visibility.
35  */
36 @SysUISingleton
37 class WindowManagerLockscreenVisibilityManager
38 @Inject
39 constructor(
40     @Main private val executor: Executor,
41     private val activityTaskManagerService: IActivityTaskManager,
42     private val keyguardStateController: KeyguardStateController,
43     private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
44     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
45 ) {
46 
47     /**
48      * Whether the lockscreen is showing, which we pass to [IActivityTaskManager.setLockScreenShown]
49      * in order to show the lockscreen and hide the surface behind the keyguard (or the inverse).
50      *
51      * This value is null if we have not yet called setLockScreenShown with any value. This will
52      * happen during the boot sequence, but we can't default to true here since otherwise we'll
53      * short-circuit on the first call to setLockScreenShown since we'll think we're already
54      * showing.
55      */
56     private var isLockscreenShowing: Boolean? = null
57 
58     /**
59      * Whether AOD is showing, which we pass to [IActivityTaskManager.setLockScreenShown] in order
60      * to show AOD when the lockscreen is visible.
61      */
62     private var isAodVisible = false
63 
64     /**
65      * Whether the keyguard is currently "going away", which we triggered via a call to
66      * [IActivityTaskManager.keyguardGoingAway]. When we tell WM that the keyguard is going away,
67      * the app/launcher surface behind the keyguard is made visible, and WM calls
68      * [onKeyguardGoingAwayRemoteAnimationStart] with a RemoteAnimationTarget so that we can animate
69      * it.
70      *
71      * Going away does not inherently result in [isLockscreenShowing] being set to false; we need to
72      * do that ourselves once we are done animating the surface.
73      *
74      * THIS IS THE ONLY PLACE 'GOING AWAY' TERMINOLOGY SHOULD BE USED. 'Going away' is a WM concept
75      * and we have gotten into trouble using it to mean various different things in the past. Unlock
76      * animations may still be visible when the keyguard is NOT 'going away', for example, when we
77      * play in-window animations, we set the surface to alpha=1f and end the animation immediately.
78      * The remainder of the animation occurs in-window, so while you might expect that the keyguard
79      * is still 'going away' because unlock animations are playing, it's actually not.
80      *
81      * If you want to know if the keyguard is 'going away', you probably want to check if we have
82      * STARTED but not FINISHED a transition to GONE.
83      *
84      * The going away animation will run until:
85      * - We manually call [endKeyguardGoingAwayAnimation] after we're done animating.
86      * - We call [setLockscreenShown] = true, which cancels the going away animation.
87      * - WM calls [onKeyguardGoingAwayRemoteAnimationCancelled] for another reason (such as the 10
88      *   second timeout).
89      */
90     private var isKeyguardGoingAway = false
91         private set(value) {
92             // TODO(b/278086361): Extricate the keyguard state controller.
93             keyguardStateController.notifyKeyguardGoingAway(value)
94             field = value
95         }
96 
97     /** Callback provided by WM to call once we're done with the going away animation. */
98     private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
99 
100     /**
101      * Set the visibility of the surface behind the keyguard, making the appropriate calls to Window
102      * Manager to effect the change.
103      */
setSurfaceBehindVisibilitynull104     fun setSurfaceBehindVisibility(visible: Boolean) {
105         if (isKeyguardGoingAway == visible) {
106             Log.d(TAG, "WmLockscreenVisibilityManager#setVisibility -> already visible=$visible")
107             return
108         }
109 
110         // The surface behind is always visible if the lockscreen is not showing, so we're already
111         // visible.
112         if (visible && isLockscreenShowing != true) {
113             Log.d(TAG, "#setVisibility -> already visible since the lockscreen isn't showing")
114             return
115         }
116 
117         if (visible) {
118             // Make the surface visible behind the keyguard by calling keyguardGoingAway. The
119             // lockscreen is still showing as well, allowing us to animate unlocked.
120             Log.d(TAG, "ActivityTaskManagerService#keyguardGoingAway()")
121             activityTaskManagerService.keyguardGoingAway(0)
122             isKeyguardGoingAway = true
123         } else {
124             // Hide the surface by setting the lockscreen showing.
125             setLockscreenShown(true)
126         }
127     }
128 
setAodVisiblenull129     fun setAodVisible(aodVisible: Boolean) {
130         setWmLockscreenState(aodVisible = aodVisible)
131     }
132 
133     /** Sets the visibility of the lockscreen. */
setLockscreenShownnull134     fun setLockscreenShown(lockscreenShown: Boolean) {
135         setWmLockscreenState(lockscreenShowing = lockscreenShown)
136     }
137 
onKeyguardGoingAwayRemoteAnimationStartnull138     fun onKeyguardGoingAwayRemoteAnimationStart(
139         @WindowManager.TransitionOldType transit: Int,
140         apps: Array<RemoteAnimationTarget>,
141         wallpapers: Array<RemoteAnimationTarget>,
142         nonApps: Array<RemoteAnimationTarget>,
143         finishedCallback: IRemoteAnimationFinishedCallback
144     ) {
145         // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
146         // going away animation on its own, if an activity launches and then requests dismissing the
147         // keyguard. In this case, this is the first and only signal we'll receive to start
148         // a transition to GONE. This transition needs to start even if we're not provided an app
149         // animation target - it's possible the app is destroyed on creation, etc. but we'll still
150         // be unlocking.
151         keyguardTransitionInteractor.startDismissKeyguardTransition(
152             reason = "Going away remote animation started"
153         )
154 
155         if (apps.isNotEmpty()) {
156             goingAwayRemoteAnimationFinishedCallback = finishedCallback
157             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
158         } else {
159             // Nothing to do here if we have no apps, end the animation, which will cancel it and WM
160             // will make *something* visible.
161             finishedCallback.onAnimationFinished()
162         }
163     }
164 
onKeyguardGoingAwayRemoteAnimationCancellednull165     fun onKeyguardGoingAwayRemoteAnimationCancelled() {
166         // If WM cancelled the animation, we need to end immediately even if we're still using the
167         // animation.
168         endKeyguardGoingAwayAnimation()
169     }
170 
171     /**
172      * Whether the going away remote animation target is in-use, which means we're animating it or
173      * intend to animate it.
174      *
175      * Some unlock animations (such as the translation spring animation) are non-deterministic and
176      * might end after the transition to GONE ends. In that case, we want to keep the remote
177      * animation running until the spring ends.
178      */
setUsingGoingAwayRemoteAnimationnull179     fun setUsingGoingAwayRemoteAnimation(usingTarget: Boolean) {
180         if (!usingTarget) {
181             endKeyguardGoingAwayAnimation()
182         }
183     }
184 
185     /**
186      * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
187      *
188      * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This
189      * will be decided by the [KeyguardTransitionBootInteractor] shortly.
190      */
setWmLockscreenStatenull191     private fun setWmLockscreenState(
192         lockscreenShowing: Boolean? = this.isLockscreenShowing,
193         aodVisible: Boolean = this.isAodVisible
194     ) {
195         Log.d(
196             TAG,
197             "#setWmLockscreenState(" +
198                 "isLockscreenShowing=$lockscreenShowing, " +
199                 "aodVisible=$aodVisible)."
200         )
201 
202         if (lockscreenShowing == null) {
203             Log.d(
204                 TAG,
205                 "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
206                     "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
207                     "will happen once KeyguardTransitionBootInteractor starts the boot transition."
208             )
209             this.isAodVisible = aodVisible
210             return
211         }
212 
213         if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
214             return
215         }
216 
217         Log.d(
218             TAG,
219             "ATMS#setLockScreenShown(" +
220                 "isLockscreenShowing=$lockscreenShowing, " +
221                 "aodVisible=$aodVisible)."
222         )
223         activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
224         this.isLockscreenShowing = lockscreenShowing
225         this.isAodVisible = aodVisible
226     }
227 
endKeyguardGoingAwayAnimationnull228     private fun endKeyguardGoingAwayAnimation() {
229         if (!isKeyguardGoingAway) {
230             Log.d(
231                 TAG,
232                 "#endKeyguardGoingAwayAnimation() called when isKeyguardGoingAway=false. " +
233                     "Short-circuiting."
234             )
235             return
236         }
237 
238         executor.execute {
239             Log.d(TAG, "Finishing remote animation.")
240             goingAwayRemoteAnimationFinishedCallback?.onAnimationFinished()
241             goingAwayRemoteAnimationFinishedCallback = null
242 
243             isKeyguardGoingAway = false
244 
245             keyguardSurfaceBehindAnimator.notifySurfaceReleased()
246         }
247     }
248 
249     companion object {
250         private val TAG = WindowManagerLockscreenVisibilityManager::class.java.simpleName
251     }
252 }
253