• 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.wm.shell.keyguard;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23 import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
24 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
25 import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
26 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
27 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
28 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
29 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
30 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
31 import static android.view.WindowManager.TRANSIT_SLEEP;
32 import static android.view.WindowManager.TRANSIT_TO_BACK;
33 import static android.view.WindowManager.TRANSIT_TO_FRONT;
34 
35 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
36 
37 import android.annotation.NonNull;
38 import android.annotation.Nullable;
39 import android.app.ActivityManager;
40 import android.os.Binder;
41 import android.os.Handler;
42 import android.os.IBinder;
43 import android.os.RemoteException;
44 import android.util.ArrayMap;
45 import android.util.Log;
46 import android.view.SurfaceControl;
47 import android.view.WindowManager;
48 import android.window.IRemoteTransition;
49 import android.window.IRemoteTransitionFinishedCallback;
50 import android.window.KeyguardState;
51 import android.window.TransitionInfo;
52 import android.window.TransitionRequestInfo;
53 import android.window.WindowContainerToken;
54 import android.window.WindowContainerTransaction;
55 
56 import com.android.internal.protolog.ProtoLog;
57 import com.android.window.flags.Flags;
58 import com.android.wm.shell.common.DisplayController;
59 import com.android.wm.shell.common.ShellExecutor;
60 import com.android.wm.shell.common.TaskStackListenerCallback;
61 import com.android.wm.shell.common.TaskStackListenerImpl;
62 import com.android.wm.shell.protolog.ShellProtoLogGroup;
63 import com.android.wm.shell.shared.annotations.ExternalThread;
64 import com.android.wm.shell.sysui.KeyguardChangeListener;
65 import com.android.wm.shell.sysui.ShellController;
66 import com.android.wm.shell.sysui.ShellInit;
67 import com.android.wm.shell.transition.FocusTransitionObserver;
68 import com.android.wm.shell.transition.Transitions;
69 import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
70 
71 /**
72  * The handler for Keyguard enter/exit and occlude/unocclude animations.
73  *
74  * <p>This takes the highest priority.
75  */
76 public class KeyguardTransitionHandler
77         implements Transitions.TransitionHandler, KeyguardChangeListener,
78         TaskStackListenerCallback {
79     private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
80             Flags.ensureKeyguardDoesTransitionStarting();
81 
82     private static final String TAG = "KeyguardTransition";
83 
84     private final Transitions mTransitions;
85     private final ShellController mShellController;
86 
87     private final DisplayController mDisplayController;
88     private final Handler mMainHandler;
89     private final ShellExecutor mMainExecutor;
90 
91     private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>();
92     private final TaskStackListenerImpl mTaskStackListener;
93     private final FocusTransitionObserver mFocusTransitionObserver;
94 
95     /**
96      * Local IRemoteTransition implementations registered by the keyguard service.
97      * @see KeyguardTransitions
98      */
99     private IRemoteTransition mExitTransition = null;
100     private IRemoteTransition mAppearTransition = null;
101     private IRemoteTransition mOccludeTransition = null;
102     private IRemoteTransition mOccludeByDreamTransition = null;
103     private IRemoteTransition mUnoccludeTransition = null;
104 
105     // While set true, Keyguard has created a remote animation runner to handle the open app
106     // transition.
107     private boolean mIsLaunchingActivityOverLockscreen;
108 
109     // Last value reported by {@link KeyguardChangeListener}.
110     private boolean mKeyguardShowing = true;
111     @Nullable
112     private WindowContainerToken mDreamToken;
113 
114     private final class StartedTransition {
115         final TransitionInfo mInfo;
116         final SurfaceControl.Transaction mFinishT;
117         final IRemoteTransition mPlayer;
118 
StartedTransition(TransitionInfo info, SurfaceControl.Transaction finishT, IRemoteTransition player)119         public StartedTransition(TransitionInfo info,
120                 SurfaceControl.Transaction finishT, IRemoteTransition player) {
121             mInfo = info;
122             mFinishT = finishT;
123             mPlayer = player;
124         }
125     }
126 
KeyguardTransitionHandler( @onNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull DisplayController displayController, @NonNull Transitions transitions, @NonNull TaskStackListenerImpl taskStackListener, @NonNull Handler mainHandler, @NonNull ShellExecutor mainExecutor, @NonNull FocusTransitionObserver focusTransitionObserver)127     public KeyguardTransitionHandler(
128             @NonNull ShellInit shellInit,
129             @NonNull ShellController shellController,
130             @NonNull DisplayController displayController,
131             @NonNull Transitions transitions,
132             @NonNull TaskStackListenerImpl taskStackListener,
133             @NonNull Handler mainHandler,
134             @NonNull ShellExecutor mainExecutor,
135             @NonNull FocusTransitionObserver focusTransitionObserver) {
136         mTransitions = transitions;
137         mShellController = shellController;
138         mDisplayController = displayController;
139         mMainHandler = mainHandler;
140         mMainExecutor = mainExecutor;
141         mTaskStackListener = taskStackListener;
142         shellInit.addInitCallback(this::onInit, this);
143         mFocusTransitionObserver = focusTransitionObserver;
144     }
145 
onInit()146     private void onInit() {
147         mTransitions.addHandler(this);
148         mShellController.addKeyguardChangeListener(this);
149         if (dismissDreamOnKeyguardDismiss()) {
150             mTaskStackListener.addListener(this);
151         }
152     }
153 
154     /**
155      * Interface for SystemUI implementations to set custom Keyguard exit/occlude handlers.
156      */
157     @ExternalThread
asKeyguardTransitions()158     public KeyguardTransitions asKeyguardTransitions() {
159         return new KeyguardTransitionsImpl();
160     }
161 
handles(TransitionInfo info)162     public static boolean handles(TransitionInfo info) {
163         // There is no animation for screen-wake unless we are immediately unlocking.
164         if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
165             return false;
166         }
167         return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0;
168     }
169 
170     @Override
onKeyguardVisibilityChanged( boolean visible, boolean occluded, boolean animatingDismiss)171     public void onKeyguardVisibilityChanged(
172             boolean visible, boolean occluded, boolean animatingDismiss) {
173         mKeyguardShowing = visible;
174     }
175 
isKeyguardShowing()176     public boolean isKeyguardShowing() {
177         return mKeyguardShowing;
178     }
179 
isKeyguardAnimating()180     public boolean isKeyguardAnimating() {
181         return !mStartedTransitions.isEmpty();
182     }
183 
184     @Override
onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)185     public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
186         mDreamToken = taskInfo.getActivityType() == ACTIVITY_TYPE_DREAM ? taskInfo.token : null;
187     }
188 
189     @Override
startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback)190     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
191             @NonNull SurfaceControl.Transaction startTransaction,
192             @NonNull SurfaceControl.Transaction finishTransaction,
193             @NonNull TransitionFinishCallback finishCallback) {
194         if (!handles(info)) {
195             return false;
196         }
197 
198         // Choose a transition applicable for the changes and keyguard state.
199         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
200             return startAnimation(mExitTransition, "going-away",
201                     transition, info, startTransaction, finishTransaction, finishCallback);
202         }
203 
204         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0
205                 || (info.getFlags() & TRANSIT_FLAG_AOD_APPEARING) != 0) {
206             return startAnimation(mAppearTransition, "appearing",
207                     transition, info, startTransaction, finishTransaction, finishCallback);
208         }
209 
210         if (mIsLaunchingActivityOverLockscreen) {
211             return false;
212         }
213 
214         // Occlude/unocclude animations are only played if the keyguard is locked.
215         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
216             if (isKeyguardOccluding(info)) {
217                 if (hasOpeningDream(info)) {
218                     return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
219                             transition, info, startTransaction, finishTransaction, finishCallback);
220                 } else {
221                     return startAnimation(mOccludeTransition, "occlude",
222                             transition, info, startTransaction, finishTransaction, finishCallback);
223                 }
224             } else if (isKeyguardUnoccluding(info)) {
225                 return startAnimation(mUnoccludeTransition, "unocclude",
226                         transition, info, startTransaction, finishTransaction, finishCallback);
227             }
228         }
229 
230         Log.i(TAG, "Refused to play keyguard transition: " + info);
231         return false;
232     }
233 
startAnimation(IRemoteTransition remoteHandler, String description, @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback)234     private boolean startAnimation(IRemoteTransition remoteHandler, String description,
235             @NonNull IBinder transition, @NonNull TransitionInfo info,
236             @NonNull SurfaceControl.Transaction startTransaction,
237             @NonNull SurfaceControl.Transaction finishTransaction,
238             @NonNull TransitionFinishCallback finishCallback) {
239 
240         if (remoteHandler == null) {
241             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
242                     "missing handler for keyguard %s transition", description);
243             return false;
244         }
245 
246         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
247                 "start keyguard %s transition, info = %s", description, info);
248         try {
249             mStartedTransitions.put(transition,
250                     new StartedTransition(info, finishTransaction, remoteHandler));
251             remoteHandler.startAnimation(transition, info, startTransaction,
252                     new IRemoteTransitionFinishedCallback.Stub() {
253                         @Override
254                         public void onTransitionFinished(
255                                 WindowContainerTransaction wct, SurfaceControl.Transaction sct) {
256                             if (sct != null) {
257                                 finishTransaction.merge(sct);
258                             }
259                             final WindowContainerTransaction mergedWct =
260                                     new WindowContainerTransaction();
261                             if (wct != null) {
262                                 mergedWct.merge(wct, true);
263                             }
264                             maybeDismissFreeformOccludingKeyguard(mergedWct, info);
265                             // Post our finish callback to let startAnimation finish first.
266                             mMainExecutor.executeDelayed(() -> {
267                                 mStartedTransitions.remove(transition);
268                                 finishCallback.onTransitionFinished(mergedWct);
269                             }, 0);
270                         }
271                     });
272         } catch (RemoteException e) {
273             Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e);
274             return false;
275         }
276         startTransaction.clear();
277         return true;
278     }
279 
280     @Override
mergeAnimation(@onNull IBinder nextTransition, @NonNull TransitionInfo nextInfo, @NonNull SurfaceControl.Transaction nextT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder currentTransition, @NonNull TransitionFinishCallback nextFinishCallback)281     public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo,
282             @NonNull SurfaceControl.Transaction nextT, @NonNull SurfaceControl.Transaction finishT,
283             @NonNull IBinder currentTransition,
284             @NonNull TransitionFinishCallback nextFinishCallback) {
285         final StartedTransition playing = mStartedTransitions.get(currentTransition);
286         if (playing == null) {
287             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
288                     "unknown keyguard transition %s", currentTransition);
289             return;
290         }
291         if ((nextInfo.getFlags() & WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING) != 0
292                 && (playing.mInfo.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
293             // Keyguard unlocking has been canceled. Merge the unlock and re-lock transitions to
294             // avoid a flicker where we flash one frame with the screen fully unlocked.
295             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
296                     "canceling keyguard exit transition %s", currentTransition);
297             playing.mFinishT.merge(nextT);
298             try {
299                 playing.mPlayer.mergeAnimation(nextTransition, nextInfo, nextT, currentTransition,
300                         new FakeFinishCallback());
301             } catch (RemoteException e) {
302                 // There is no good reason for this to happen because the player is a local object
303                 // implementing an AIDL interface.
304                 Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
305             }
306             nextFinishCallback.onTransitionFinished(null);
307         } else {
308             // In all other cases, fast-forward to let the next queued transition start playing.
309             finishAnimationImmediately(currentTransition, playing);
310         }
311     }
312 
313     @Override
onTransitionConsumed(IBinder transition, boolean aborted, SurfaceControl.Transaction finishTransaction)314     public void onTransitionConsumed(IBinder transition, boolean aborted,
315             SurfaceControl.Transaction finishTransaction) {
316         final StartedTransition playing = mStartedTransitions.remove(transition);
317         if (playing != null) {
318             finishAnimationImmediately(transition, playing);
319         }
320     }
321 
322     @Nullable
323     @Override
handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)324     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
325             @NonNull TransitionRequestInfo request) {
326         if (dismissDreamOnKeyguardDismiss()
327                 && (request.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0
328                 && mDreamToken != null) {
329             // Dismiss the dream in the same transaction, so that it isn't visible once the device
330             // is unlocked.
331             return new WindowContainerTransaction().removeTask(mDreamToken);
332         }
333         return null;
334     }
335 
hasOpeningDream(@onNull TransitionInfo info)336     private static boolean hasOpeningDream(@NonNull TransitionInfo info) {
337         for (int i = info.getChanges().size() - 1; i >= 0; i--) {
338             final TransitionInfo.Change change = info.getChanges().get(i);
339             if (isOpeningType(change.getMode())
340                     && change.getTaskInfo() != null
341                     && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) {
342                 return true;
343             }
344         }
345         return false;
346     }
347 
isKeyguardOccluding(@onNull TransitionInfo info)348     private static boolean isKeyguardOccluding(@NonNull TransitionInfo info) {
349         if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
350             return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0;
351         }
352 
353         for (int i = 0; i < info.getChanges().size(); i++) {
354             TransitionInfo.Change change = info.getChanges().get(i);
355             if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
356                     && change.getMode() == TRANSIT_TO_FRONT) {
357                 return true;
358             }
359         }
360         return false;
361     }
362 
isKeyguardUnoccluding(@onNull TransitionInfo info)363     private static boolean isKeyguardUnoccluding(@NonNull TransitionInfo info) {
364         if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
365             return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0;
366         }
367 
368         for (int i = 0; i < info.getChanges().size(); i++) {
369             TransitionInfo.Change change = info.getChanges().get(i);
370             if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
371                     && change.getMode() == TRANSIT_TO_BACK) {
372                 return true;
373             }
374         }
375         return false;
376     }
377 
finishAnimationImmediately(IBinder transition, StartedTransition playing)378     private void finishAnimationImmediately(IBinder transition, StartedTransition playing) {
379         final IBinder fakeTransition = new Binder();
380         final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0);
381         final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction();
382         final FakeFinishCallback fakeFinishCb = new FakeFinishCallback();
383         try {
384             playing.mPlayer.mergeAnimation(
385                     fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb);
386         } catch (RemoteException e) {
387             // There is no good reason for this to happen because the player is a local object
388             // implementing an AIDL interface.
389             Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
390         }
391     }
392 
maybeDismissFreeformOccludingKeyguard( WindowContainerTransaction wct, TransitionInfo info)393     private void maybeDismissFreeformOccludingKeyguard(
394             WindowContainerTransaction wct, TransitionInfo info) {
395         if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) == 0) {
396             return;
397         }
398         // There's a window occluding the Keyguard, find it and if it's in freeform mode, change it
399         // to fullscreen.
400         for (int i = 0; i < info.getChanges().size(); i++) {
401             final TransitionInfo.Change change = info.getChanges().get(i);
402             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
403             if (taskInfo != null && taskInfo.taskId != INVALID_TASK_ID
404                     && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
405                     && mFocusTransitionObserver.hasGlobalFocus(taskInfo)
406                     && change.getContainer() != null) {
407                 wct.setWindowingMode(change.getContainer(), WINDOWING_MODE_FULLSCREEN);
408                 wct.setBounds(change.getContainer(), null);
409                 return;
410             }
411         }
412     }
413 
414     private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub {
415         @Override
onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction t)416         public void onTransitionFinished(
417                 WindowContainerTransaction wct, SurfaceControl.Transaction t) {
418             return;
419         }
420     }
421 
422     @ExternalThread
423     private final class KeyguardTransitionsImpl implements KeyguardTransitions {
424         @Override
register( IRemoteTransition exitTransition, IRemoteTransition appearTransition, IRemoteTransition occludeTransition, IRemoteTransition occludeByDreamTransition, IRemoteTransition unoccludeTransition)425         public void register(
426                 IRemoteTransition exitTransition,
427                 IRemoteTransition appearTransition,
428                 IRemoteTransition occludeTransition,
429                 IRemoteTransition occludeByDreamTransition,
430                 IRemoteTransition unoccludeTransition) {
431             mMainExecutor.execute(() -> {
432                 mExitTransition = exitTransition;
433                 mAppearTransition = appearTransition;
434                 mOccludeTransition = occludeTransition;
435                 mOccludeByDreamTransition = occludeByDreamTransition;
436                 mUnoccludeTransition = unoccludeTransition;
437             });
438         }
439 
440         @Override
setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen)441         public void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
442             mMainExecutor.execute(() ->
443                     mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen);
444         }
445 
446         @Override
startKeyguardTransition(boolean keyguardShowing, boolean aodShowing)447         public void startKeyguardTransition(boolean keyguardShowing, boolean aodShowing) {
448             final WindowContainerTransaction wct = new WindowContainerTransaction();
449             wct.addKeyguardState(new KeyguardState.Builder().setKeyguardShowing(keyguardShowing)
450                     .setAodShowing(aodShowing).build());
451             mMainExecutor.execute(() -> {
452                 mTransitions.startTransition(keyguardShowing ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
453                         wct, KeyguardTransitionHandler.this);
454             });
455         }
456     }
457 }
458