• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.transition;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
22 import static android.view.WindowManager.TRANSIT_CHANGE;
23 import static android.view.WindowManager.TRANSIT_CLOSE;
24 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
25 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
26 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
27 import static android.view.WindowManager.TRANSIT_OPEN;
28 import static android.view.WindowManager.TRANSIT_SLEEP;
29 import static android.view.WindowManager.TRANSIT_TO_BACK;
30 import static android.view.WindowManager.TRANSIT_TO_FRONT;
31 import static android.view.WindowManager.fixScale;
32 import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
33 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
34 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
35 import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
36 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
37 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
38 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
39 
40 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
41 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
42 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
43 import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
44 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
45 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
46 
47 import android.annotation.NonNull;
48 import android.annotation.Nullable;
49 import android.app.ActivityTaskManager;
50 import android.app.AppGlobals;
51 import android.app.IApplicationThread;
52 import android.content.ContentResolver;
53 import android.content.Context;
54 import android.content.pm.PackageManager;
55 import android.database.ContentObserver;
56 import android.os.Build;
57 import android.os.Handler;
58 import android.os.IBinder;
59 import android.os.RemoteException;
60 import android.os.SystemProperties;
61 import android.os.Trace;
62 import android.provider.Settings;
63 import android.util.ArrayMap;
64 import android.util.Log;
65 import android.util.Pair;
66 import android.view.SurfaceControl;
67 import android.view.WindowManager;
68 import android.window.ITransitionPlayer;
69 import android.window.RemoteTransition;
70 import android.window.TaskFragmentOrganizer;
71 import android.window.TransitionFilter;
72 import android.window.TransitionInfo;
73 import android.window.TransitionMetrics;
74 import android.window.TransitionRequestInfo;
75 import android.window.WindowAnimationState;
76 import android.window.WindowContainerTransaction;
77 
78 import androidx.annotation.BinderThread;
79 
80 import com.android.internal.R;
81 import com.android.internal.annotations.VisibleForTesting;
82 import com.android.internal.jank.InteractionJankMonitor;
83 import com.android.internal.protolog.ProtoLog;
84 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
85 import com.android.wm.shell.ShellTaskOrganizer;
86 import com.android.wm.shell.common.DisplayController;
87 import com.android.wm.shell.common.DisplayInsetsController;
88 import com.android.wm.shell.common.ExternalInterfaceBinder;
89 import com.android.wm.shell.common.RemoteCallable;
90 import com.android.wm.shell.common.ShellExecutor;
91 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes;
92 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
93 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
94 import com.android.wm.shell.protolog.ShellProtoLogGroup;
95 import com.android.wm.shell.shared.FocusTransitionListener;
96 import com.android.wm.shell.shared.IFocusTransitionListener;
97 import com.android.wm.shell.shared.IHomeTransitionListener;
98 import com.android.wm.shell.shared.IShellTransitions;
99 import com.android.wm.shell.shared.ShellTransitions;
100 import com.android.wm.shell.shared.TransactionPool;
101 import com.android.wm.shell.shared.TransitionUtil;
102 import com.android.wm.shell.shared.annotations.ExternalThread;
103 import com.android.wm.shell.sysui.ShellCommandHandler;
104 import com.android.wm.shell.sysui.ShellController;
105 import com.android.wm.shell.sysui.ShellInit;
106 import com.android.wm.shell.transition.tracing.LegacyTransitionTracer;
107 import com.android.wm.shell.transition.tracing.PerfettoTransitionTracer;
108 import com.android.wm.shell.transition.tracing.TransitionTracer;
109 
110 import java.io.PrintWriter;
111 import java.util.ArrayList;
112 import java.util.Arrays;
113 import java.util.concurrent.Executor;
114 
115 /**
116  * Plays transition animations. Within this player, each transition has a lifecycle.
117  * 1. When a transition is directly started or requested, it is added to "pending" state.
118  * 2. Once WMCore applies the transition and notifies, the transition moves to "ready" state.
119  * 3. When a transition starts animating, it is moved to the "active" state.
120  *
121  * Basically: --start--> PENDING --onTransitionReady--> READY --play--> ACTIVE --finish--> |
122  *                                                            --merge--> MERGED --^
123  *
124  * The READY and beyond lifecycle is managed per "track". Within a track, all the animations are
125  * serialized as described; however, multiple tracks can play simultaneously. This implies that,
126  * within a track, only one transition can be animating ("active") at a time.
127  *
128  * While a transition is animating in a track, transitions dispatched to the track will be queued
129  * in the "ready" state for their turn. At the same time, whenever a transition makes it to the
130  * head of the "ready" queue, it will attempt to merge to with the "active" transition. If the
131  * merge succeeds, it will be moved to the "active" transition's "merged" list and then the next
132  * "ready" transition can attempt to merge. Once the "active" transition animation is finished,
133  * the next "ready" transition can play.
134  *
135  * Track assignments are expected to be provided by WMCore and this generally tries to maintain
136  * the same assignments. If, however, WMCore decides that a transition conflicts with >1 active
137  * track, it will be marked as SYNC. This means that all currently active tracks must be flushed
138  * before the SYNC transition can play.
139  */
140 public class Transitions implements RemoteCallable<Transitions>,
141         ShellCommandHandler.ShellCommandActionHandler {
142     static final String TAG = "ShellTransitions";
143 
144     // If set, will print the stack trace for transition starts within the process
145     static final boolean DEBUG_START_TRANSITION = Build.IS_DEBUGGABLE &&
146             SystemProperties.getBoolean("persist.wm.debug.start_shell_transition", false);
147 
148     /** Set to {@code true} to enable shell transitions. */
149     public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
150     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
151             && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
152 
153     /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
154     public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
155 
156     public static final int TRANSIT_EXIT_PIP_TO_SPLIT =  TRANSIT_FIRST_CUSTOM + 2;
157 
158     /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
159     public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
160 
161     /** Transition type for launching 2 tasks simultaneously. */
162     public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
163 
164     /** Transition type for entering split by opening an app into side-stage. */
165     public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
166 
167     /** Transition type for dismissing split-screen via dragging the divider off the screen. */
168     public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
169 
170     /** Transition type for dismissing split-screen. */
171     public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
172 
173     /** Transition type for freeform to maximize transition. */
174     public static final int TRANSIT_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 8;
175 
176     /** Transition type for maximize to freeform transition. */
177     public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
178 
179     /** Transition to resize PiP task. */
180     public static final int TRANSIT_RESIZE_PIP = TRANSIT_FIRST_CUSTOM + 16;
181 
182     /**
183      * The task fragment drag resize transition used by activity embedding.
184      */
185     public static final int TRANSIT_TASK_FRAGMENT_DRAG_RESIZE =
186             // TRANSIT_FIRST_CUSTOM + 17
187             TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE;
188 
189     /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
190     public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
191 
192     /** Transition to set windowing mode after exit pip transition is finished animating. */
193     public static final int TRANSIT_CLEANUP_PIP_EXIT = WindowManager.TRANSIT_FIRST_CUSTOM + 19;
194 
195     /** Transition type to minimize a task. */
196     public static final int TRANSIT_MINIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 20;
197 
198     /** Transition to start the recents transition */
199     public static final int TRANSIT_START_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 21;
200 
201     /** Transition to end the recents transition */
202     public static final int TRANSIT_END_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 22;
203 
204     /** Transition type for app compat reachability. */
205     public static final int TRANSIT_MOVE_LETTERBOX_REACHABILITY = TRANSIT_FIRST_CUSTOM + 23;
206 
207     /** Transition type for converting a task to a bubble. */
208     public static final int TRANSIT_CONVERT_TO_BUBBLE = TRANSIT_FIRST_CUSTOM + 24;
209 
210     /** Transition type for desktop mode transitions. */
211     public static final int TRANSIT_DESKTOP_MODE_TYPES =
212             WindowManager.TRANSIT_FIRST_CUSTOM + 100;
213 
214     private final ShellTaskOrganizer mOrganizer;
215     private final Context mContext;
216     private final ShellExecutor mMainExecutor;
217     private final ShellExecutor mAnimExecutor;
218     private final Handler mAnimHandler;
219     private final TransitionPlayerImpl mPlayerImpl;
220     private final DefaultTransitionHandler mDefaultTransitionHandler;
221     private final RemoteTransitionHandler mRemoteTransitionHandler;
222     private final DisplayController mDisplayController;
223     private final ShellCommandHandler mShellCommandHandler;
224     private final ShellController mShellController;
225     private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
226     private final SleepHandler mSleepHandler = new SleepHandler();
227     private final TransitionTracer mTransitionTracer;
228     private boolean mIsRegistered = false;
229 
230     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
231     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
232 
233     private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
234 
235     private HomeTransitionObserver mHomeTransitionObserver;
236     private FocusTransitionObserver mFocusTransitionObserver;
237 
238     /** List of {@link Runnable} instances to run when the last active transition has finished.  */
239     private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
240 
241     private float mTransitionAnimationScaleSetting = 1.0f;
242 
243     /**
244      * How much time we allow for an animation to finish itself on sync. If it takes longer, we
245      * will force-finish it (on this end) which may leave it in a bad state but won't hang the
246      * device. This needs to be pretty small because it is an allowance for each queued animation,
247      * however it can't be too small since there is some potential IPC involved.
248      */
249     private static final int SYNC_ALLOWANCE_MS = 120;
250 
251     /** For testing only. Disables the force-finish timeout on sync. */
252     private boolean mDisableForceSync = false;
253 
254     private static final class ActiveTransition {
255         final IBinder mToken;
256 
257         TransitionHandler mHandler;
258         boolean mAborted;
259         TransitionInfo mInfo;
260         SurfaceControl.Transaction mStartT;
261         SurfaceControl.Transaction mFinishT;
262 
263         /** Ordered list of transitions which have been merged into this one. */
264         private ArrayList<ActiveTransition> mMerged;
265 
ActiveTransition(IBinder token)266         ActiveTransition(IBinder token) {
267             mToken = token;
268         }
269 
isSync()270         boolean isSync() {
271             return (mInfo.getFlags() & TransitionInfo.FLAG_SYNC) != 0;
272         }
273 
getTrack()274         int getTrack() {
275             return mInfo != null ? mInfo.getTrack() : -1;
276         }
277 
278         @Override
toString()279         public String toString() {
280             if (mInfo != null && mInfo.getDebugId() >= 0) {
281                 return "(#" + mInfo.getDebugId() + ") " + mToken + "@" + getTrack();
282             }
283             return mToken.toString() + "@" + getTrack();
284         }
285     }
286 
287     private static class Track {
288         /** Keeps track of transitions which are ready to play but still waiting for their turn. */
289         final ArrayList<ActiveTransition> mReadyTransitions = new ArrayList<>();
290 
291         /** The currently playing transition in this track. */
292         ActiveTransition mActiveTransition = null;
293 
isIdle()294         boolean isIdle() {
295             return mActiveTransition == null && mReadyTransitions.isEmpty();
296         }
297     }
298 
299     /** All transitions that we have created, but not yet finished. */
300     private final ArrayMap<IBinder, ActiveTransition> mKnownTransitions = new ArrayMap<>();
301 
302     /** Keeps track of transitions which have been started, but aren't ready yet. */
303     private final ArrayList<ActiveTransition> mPendingTransitions = new ArrayList<>();
304 
305     /**
306      * Transitions which are ready to play, but haven't been sent to a track yet because a sync
307      * is ongoing.
308      */
309     private final ArrayList<ActiveTransition> mReadyDuringSync = new ArrayList<>();
310 
311     private final ArrayList<Track> mTracks = new ArrayList<>();
312 
Transitions(@onNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull ShellTaskOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull DisplayInsetsController displayInsetsController, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, @NonNull Handler animHandler, @NonNull HomeTransitionObserver homeTransitionObserver, @NonNull FocusTransitionObserver focusTransitionObserver)313     public Transitions(@NonNull Context context,
314             @NonNull ShellInit shellInit,
315             @NonNull ShellController shellController,
316             @NonNull ShellTaskOrganizer organizer,
317             @NonNull TransactionPool pool,
318             @NonNull DisplayController displayController,
319             @NonNull DisplayInsetsController displayInsetsController,
320             @NonNull ShellExecutor mainExecutor,
321             @NonNull Handler mainHandler,
322             @NonNull ShellExecutor animExecutor,
323             @NonNull Handler animHandler,
324             @NonNull HomeTransitionObserver homeTransitionObserver,
325             @NonNull FocusTransitionObserver focusTransitionObserver) {
326         this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
327                 displayController, displayInsetsController, mainExecutor, mainHandler, animExecutor,
328                 animHandler, new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit),
329                 homeTransitionObserver, focusTransitionObserver);
330     }
331 
Transitions(@onNull Context context, @NonNull ShellInit shellInit, @Nullable ShellCommandHandler shellCommandHandler, @NonNull ShellController shellController, @NonNull ShellTaskOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull DisplayInsetsController displayInsetsController, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, @NonNull Handler animHandler, @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer, @NonNull HomeTransitionObserver homeTransitionObserver, @NonNull FocusTransitionObserver focusTransitionObserver)332     public Transitions(@NonNull Context context,
333             @NonNull ShellInit shellInit,
334             @Nullable ShellCommandHandler shellCommandHandler,
335             @NonNull ShellController shellController,
336             @NonNull ShellTaskOrganizer organizer,
337             @NonNull TransactionPool pool,
338             @NonNull DisplayController displayController,
339             @NonNull DisplayInsetsController displayInsetsController,
340             @NonNull ShellExecutor mainExecutor,
341             @NonNull Handler mainHandler,
342             @NonNull ShellExecutor animExecutor,
343             @NonNull Handler animHandler,
344             @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer,
345             @NonNull HomeTransitionObserver homeTransitionObserver,
346             @NonNull FocusTransitionObserver focusTransitionObserver) {
347         mOrganizer = organizer;
348         mContext = context;
349         mMainExecutor = mainExecutor;
350         mAnimExecutor = animExecutor;
351         mAnimHandler = animHandler;
352         mDisplayController = displayController;
353         mPlayerImpl = new TransitionPlayerImpl();
354         mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
355                 displayController, displayInsetsController, pool, mainExecutor, mainHandler,
356                 animExecutor, mAnimHandler, rootTDAOrganizer, InteractionJankMonitor.getInstance());
357         mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
358         mShellCommandHandler = shellCommandHandler;
359         mShellController = shellController;
360         // The very last handler (0 in the list) should be the default one.
361         mHandlers.add(mDefaultTransitionHandler);
362         ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Default");
363         // Next lowest priority is remote transitions.
364         mHandlers.add(mRemoteTransitionHandler);
365         ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Remote");
366         shellInit.addInitCallback(this::onInit, this);
367         mHomeTransitionObserver = homeTransitionObserver;
368         mFocusTransitionObserver = focusTransitionObserver;
369 
370         if (android.tracing.Flags.perfettoTransitionTracing()) {
371             mTransitionTracer = new PerfettoTransitionTracer();
372         } else {
373             mTransitionTracer = new LegacyTransitionTracer();
374         }
375     }
376 
onInit()377     private void onInit() {
378         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
379             mOrganizer.shareTransactionQueue();
380         }
381         mShellController.addExternalInterface(IShellTransitions.DESCRIPTOR,
382                 this::createExternalInterface, this);
383 
384         ContentResolver resolver = mContext.getContentResolver();
385         mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
386         dispatchAnimScaleSetting(mTransitionAnimationScaleSetting);
387 
388         resolver.registerContentObserver(
389                 Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
390                 new SettingsObserver());
391 
392         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
393             mIsRegistered = true;
394             // Register this transition handler with Core
395             try {
396                 mOrganizer.registerTransitionPlayer(mPlayerImpl);
397             } catch (RuntimeException e) {
398                 mIsRegistered = false;
399                 throw e;
400             }
401             // Pre-load the instance.
402             TransitionMetrics.getInstance();
403         }
404 
405         mShellCommandHandler.addCommandCallback("transitions", this, this);
406         mShellCommandHandler.addDumpCallback(this::dump, this);
407     }
408 
isRegistered()409     public boolean isRegistered() {
410         return mIsRegistered;
411     }
412 
getTransitionAnimationScaleSetting()413     private float getTransitionAnimationScaleSetting() {
414         return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
415                 Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
416                                 R.dimen.config_appTransitionAnimationDurationScaleDefault)));
417     }
418 
asRemoteTransitions()419     public ShellTransitions asRemoteTransitions() {
420         return mImpl;
421     }
422 
createExternalInterface()423     private ExternalInterfaceBinder createExternalInterface() {
424         return new IShellTransitionsImpl(this);
425     }
426 
427     @Override
getContext()428     public Context getContext() {
429         return mContext;
430     }
431 
432     @Override
getRemoteCallExecutor()433     public ShellExecutor getRemoteCallExecutor() {
434         return mMainExecutor;
435     }
436 
dispatchAnimScaleSetting(float scale)437     private void dispatchAnimScaleSetting(float scale) {
438         for (int i = mHandlers.size() - 1; i >= 0; --i) {
439             mHandlers.get(i).setAnimScaleSetting(scale);
440         }
441     }
442 
443     /**
444      * Adds a handler candidate.
445      * @see TransitionHandler
446      */
addHandler(@onNull TransitionHandler handler)447     public void addHandler(@NonNull TransitionHandler handler) {
448         if (mHandlers.isEmpty()) {
449             throw new RuntimeException("Unexpected handler added prior to initialization, please "
450                     + "use ShellInit callbacks to ensure proper ordering");
451         }
452         mHandlers.add(handler);
453         // Set initial scale settings.
454         handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
455         ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: %s",
456                 handler.getClass().getSimpleName());
457     }
458 
getMainExecutor()459     public ShellExecutor getMainExecutor() {
460         return mMainExecutor;
461     }
462 
getAnimExecutor()463     public ShellExecutor getAnimExecutor() {
464         return mAnimExecutor;
465     }
466 
467     /** Only use this in tests. This is used to avoid running animations during tests. */
468     @VisibleForTesting
replaceDefaultHandlerForTest(TransitionHandler handler)469     void replaceDefaultHandlerForTest(TransitionHandler handler) {
470         mHandlers.set(0, handler);
471     }
472 
473     /**
474      * Register a remote transition to be used for all operations except takeovers when `filter`
475      * matches an incoming transition.
476      */
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)477     public void registerRemote(@NonNull TransitionFilter filter,
478             @NonNull RemoteTransition remoteTransition) {
479         mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
480     }
481 
482     /**
483      * Register a remote transition to be used for all operations except takeovers when `filter`
484      * matches an incoming transition.
485      */
registerRemoteForTakeover(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)486     public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
487             @NonNull RemoteTransition remoteTransition) {
488         mRemoteTransitionHandler.addFilteredForTakeover(filter, remoteTransition);
489     }
490 
491     /** Unregisters a remote transition and all associated filters */
unregisterRemote(@onNull RemoteTransition remoteTransition)492     public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
493         mRemoteTransitionHandler.removeFiltered(remoteTransition);
494     }
495 
getRemoteTransitionHandler()496     RemoteTransitionHandler getRemoteTransitionHandler() {
497         return mRemoteTransitionHandler;
498     }
499 
500     /** Registers an observer on the lifecycle of transitions. */
registerObserver(@onNull TransitionObserver observer)501     public void registerObserver(@NonNull TransitionObserver observer) {
502         mObservers.add(observer);
503     }
504 
505     /** Unregisters the observer. */
unregisterObserver(@onNull TransitionObserver observer)506     public void unregisterObserver(@NonNull TransitionObserver observer) {
507         mObservers.remove(observer);
508     }
509 
510     /** Boosts the process priority of remote animation player. */
setRunningRemoteTransitionDelegate(IApplicationThread appThread)511     public static void setRunningRemoteTransitionDelegate(IApplicationThread appThread) {
512         if (appThread == null) return;
513         try {
514             ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(appThread);
515         } catch (SecurityException e) {
516             Log.e(TAG, "Unable to boost animation process. This should only happen"
517                     + " during unit tests");
518         } catch (RemoteException e) {
519             e.rethrowFromSystemServer();
520         }
521     }
522 
523     /**
524      * Runs the given {@code runnable} when the last active transition has finished, or immediately
525      * if there are currently no active transitions.
526      *
527      * <p>This method should be called on the Shell main-thread, where the given {@code runnable}
528      * will be executed when the last active transition is finished.
529      */
runOnIdle(Runnable runnable)530     public void runOnIdle(Runnable runnable) {
531         if (isIdle()) {
532             runnable.run();
533         } else {
534             mRunWhenIdleQueue.add(runnable);
535         }
536     }
537 
setDisableForceSyncForTest(boolean disable)538     void setDisableForceSyncForTest(boolean disable) {
539         mDisableForceSync = disable;
540     }
541 
542     /**
543      * Sets up visibility/alpha/transforms to resemble the starting state of an animation.
544      */
setupStartState(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)545     private static void setupStartState(@NonNull TransitionInfo info,
546             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
547         boolean isOpening = isOpeningType(info.getType());
548         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
549             final TransitionInfo.Change change = info.getChanges().get(i);
550             if (change.hasFlags(FLAGS_IS_NON_APP_WINDOW & ~FLAG_IS_WALLPAPER)) {
551                 // Currently system windows are controlled by WindowState, so don't change their
552                 // surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
553                 // This includes IME (associated with app), because there may not be a transition
554                 // associated with their visibility changes, and currently they don't need a
555                 // transition animation.
556                 continue;
557             }
558             if (change.hasFlags(FLAG_IS_WALLPAPER) && !ensureWallpaperInTransitions()) {
559                 // Wallpaper is always z-ordered at bottom, and historically is not animated by
560                 // transition handlers.
561                 continue;
562             }
563             final SurfaceControl leash = change.getLeash();
564             final int mode = info.getChanges().get(i).getMode();
565 
566             if (mode == TRANSIT_TO_FRONT) {
567                 // When the window is moved to front, make sure the crop is updated to prevent it
568                 // from using the old crop.
569                 t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
570                 if (change.getContainer() != null) {
571                     // We don't want to crop on non-remotable (activity), because it can have
572                     // letterbox child surface that is position at a negative position related to
573                     // the activity's surface.
574                     t.setWindowCrop(leash, change.getEndAbsBounds().width(),
575                             change.getEndAbsBounds().height());
576                 }
577             }
578 
579             // Don't move anything that isn't independent within its parents
580             if (!TransitionInfo.isIndependent(change, info)) {
581                 if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
582                     t.show(leash);
583                     t.setMatrix(leash, 1, 0, 0, 1);
584                     t.setAlpha(leash, 1.f);
585                     t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
586                     if (change.getContainer() != null) {
587                         // We don't want to crop on non-remotable (activity), because it can have
588                         // letterbox child surface that is position at a negative position related
589                         // to the activity's surface.
590                         t.setWindowCrop(leash, change.getEndAbsBounds().width(),
591                                 change.getEndAbsBounds().height());
592                     }
593                 }
594                 continue;
595             }
596 
597             if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
598                 t.show(leash);
599                 t.setMatrix(leash, 1, 0, 0, 1);
600                 if (isOpening
601                         // If this is a transferred starting window, we want it immediately visible.
602                         && (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
603                     t.setAlpha(leash, 0.f);
604                 }
605                 finishT.show(leash);
606             } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
607                 finishT.hide(leash);
608             } else if (isOpening && mode == TRANSIT_CHANGE) {
609                 // Just in case there is a race with another animation (eg. recents finish()).
610                 // Changes are visible->visible so it's a problem if it isn't visible.
611                 t.show(leash);
612                 // If there is a transient launch followed by a launch of one of the pausing tasks,
613                 // we may end up with TRANSIT_TO_BACK followed by a CHANGE (w/ flag MOVE_TO_TOP),
614                 // but since we are hiding the leash in the finish transaction above, we should also
615                 // update the finish transaction here to reflect the change in visibility
616                 finishT.show(leash);
617             }
618         }
619     }
620 
calculateAnimLayer(@onNull TransitionInfo.Change change, int i, int numChanges, @WindowManager.TransitionType int transitType)621     static int calculateAnimLayer(@NonNull TransitionInfo.Change change, int i,
622             int numChanges, @WindowManager.TransitionType int transitType) {
623         // Put animating stuff above this line and put static stuff below it.
624         final int zSplitLine = numChanges + 1;
625         final boolean isOpening = isOpeningType(transitType);
626         final boolean isClosing = isClosingType(transitType);
627         final int mode = change.getMode();
628         // Put all the OPEN/SHOW on top
629         if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
630             if (isOpening) {
631                 // put on top
632                 return zSplitLine + numChanges - i;
633             } else if (isClosing) {
634                 // put on bottom
635                 return zSplitLine - i;
636             } else {
637                 // maintain relative ordering (put all changes in the animating layer)
638                 return zSplitLine + numChanges - i;
639             }
640         } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
641             if (isOpening) {
642                 // put on bottom and leave visible
643                 return zSplitLine - i;
644             } else {
645                 // put on top
646                 return zSplitLine + numChanges - i;
647             }
648         } else { // CHANGE or other
649             if (isClosing || TransitionUtil.isOrderOnly(change)) {
650                 // Put below CLOSE mode (in the "static" section).
651                 return zSplitLine - i;
652             } else {
653                 // Put above CLOSE mode.
654                 return zSplitLine + numChanges - i;
655             }
656         }
657     }
658 
659     /**
660      * Reparents all participants into a shared parent and orders them based on: the global transit
661      * type, their transit mode, and their destination z-order.
662      */
setupAnimHierarchy(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)663     private static void setupAnimHierarchy(@NonNull TransitionInfo info,
664             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
665         final int type = info.getType();
666         for (int i = 0; i < info.getRootCount(); ++i) {
667             t.show(info.getRoot(i).getLeash());
668         }
669         final int numChanges = info.getChanges().size();
670         // changes should be ordered top-to-bottom in z
671         for (int i = numChanges - 1; i >= 0; --i) {
672             final TransitionInfo.Change change = info.getChanges().get(i);
673             final SurfaceControl leash = change.getLeash();
674 
675             // Don't reparent anything that isn't independent within its parents
676             if (!TransitionInfo.isIndependent(change, info)) {
677                 continue;
678             }
679 
680             boolean hasParent = change.getParent() != null;
681 
682             final TransitionInfo.Root root = TransitionUtil.getRootFor(change, info);
683             if (!hasParent) {
684                 t.reparent(leash, root.getLeash());
685                 t.setPosition(leash,
686                         change.getStartAbsBounds().left - root.getOffset().x,
687                         change.getStartAbsBounds().top - root.getOffset().y);
688             }
689             final int layer = calculateAnimLayer(change, i, numChanges, type);
690             t.setLayer(leash, layer);
691         }
692     }
693 
findByToken(ArrayList<ActiveTransition> list, IBinder token)694     private static int findByToken(ArrayList<ActiveTransition> list, IBinder token) {
695         for (int i = list.size() - 1; i >= 0; --i) {
696             if (list.get(i).mToken == token) return i;
697         }
698         return -1;
699     }
700 
getOrCreateTrack(int trackId)701     private Track getOrCreateTrack(int trackId) {
702         while (trackId >= mTracks.size()) {
703             mTracks.add(new Track());
704         }
705         return mTracks.get(trackId);
706     }
707 
708     @VisibleForTesting
onTransitionReady(@onNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)709     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
710             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
711         info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
712         ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
713                 info.getDebugId(), transitionToken, info.toString("    " /* prefix */));
714         int activeIdx = findByToken(mPendingTransitions, transitionToken);
715         if (activeIdx < 0) {
716             final ActiveTransition existing = mKnownTransitions.get(transitionToken);
717             if (existing != null) {
718                 Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
719                 // The transition is already somewhere else in the pipeline, so just return here.
720                 t.apply();
721                 if (existing.mFinishT != null) {
722                     existing.mFinishT.merge(finishT);
723                 } else {
724                     existing.mFinishT = finishT;
725                 }
726                 return;
727             }
728             // This usually means the system is in a bad state and may not recover; however,
729             // there's an incentive to propagate bad states rather than crash, so we're kinda
730             // required to do the same thing I guess.
731             Log.wtf(TAG, "Got transitionReady for non-pending transition "
732                     + transitionToken + ". expecting one of "
733                     + Arrays.toString(mPendingTransitions.stream().map(
734                             activeTransition -> activeTransition.mToken).toArray()));
735             final ActiveTransition fallback = new ActiveTransition(transitionToken);
736             mKnownTransitions.put(transitionToken, fallback);
737             mPendingTransitions.add(fallback);
738             activeIdx = mPendingTransitions.size() - 1;
739         }
740         // Move from pending to ready
741         final ActiveTransition active = mPendingTransitions.remove(activeIdx);
742         active.mInfo = info;
743         active.mStartT = t;
744         active.mFinishT = finishT;
745         if (activeIdx > 0) {
746             Log.i(TAG, "Transition might be ready out-of-order " + activeIdx + " for " + active
747                     + ". This is ok if it's on a different track.");
748         }
749         if (!mReadyDuringSync.isEmpty()) {
750             mReadyDuringSync.add(active);
751         } else {
752             dispatchReady(active);
753         }
754     }
755 
756     /**
757      * Returns true if dispatching succeeded, otherwise false. Dispatching can fail if it is
758      * blocked by a sync or sleep.
759      */
dispatchReady(ActiveTransition active)760     boolean dispatchReady(ActiveTransition active) {
761         final TransitionInfo info = active.mInfo;
762 
763         if (info.getType() == TRANSIT_SLEEP || active.isSync()) {
764             // Adding to *front*! If we are here, it means that it was pulled off the front
765             // so we are just putting it back; or, it is the first one so it doesn't matter.
766             mReadyDuringSync.add(0, active);
767             boolean hadPreceding = false;
768             // Now flush all the tracks.
769             for (int i = 0; i < mTracks.size(); ++i) {
770                 final Track tr = mTracks.get(i);
771                 if (tr.isIdle()) continue;
772                 hadPreceding = true;
773                 // Sleep starts a process of forcing all prior transitions to finish immediately
774                 ProtoLog.v(WM_SHELL_TRANSITIONS,
775                         "Start finish-for-sync track %d", i);
776                 finishForSync(active.mToken, i, null /* forceFinish */);
777             }
778             if (hadPreceding) {
779                 return false;
780             }
781             // Actually able to process the sleep now, so re-remove it from the queue and continue
782             // the normal flow.
783             mReadyDuringSync.remove(active);
784         }
785 
786         // If any of the changes are on DesktopWallpaperActivity, add the flag to the change.
787         for (TransitionInfo.Change change : info.getChanges()) {
788             if (change.getTaskInfo() != null
789                     && DesktopWallpaperActivity.isWallpaperTask(change.getTaskInfo())) {
790                 change.setFlags(FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY);
791             }
792         }
793 
794         final Track track = getOrCreateTrack(info.getTrack());
795         track.mReadyTransitions.add(active);
796 
797         for (int i = 0; i < mObservers.size(); ++i) {
798             final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER);
799             if (useTrace) {
800                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
801                         mObservers.get(i).getClass().getSimpleName() + "#onTransitionReady: "
802                                 + transitTypeToString(info.getType()));
803             }
804             mObservers.get(i).onTransitionReady(
805                     active.mToken, info, active.mStartT, active.mFinishT);
806             if (useTrace) {
807                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
808             }
809         }
810 
811         /*
812          * Some transitions we always need to report to keyguard even if they are empty.
813          * TODO (b/274954192): Remove this once keyguard dispatching fully moves to Shell.
814          */
815         if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) {
816             // No root-leashes implies that the transition is empty/no-op, so just do
817             // housekeeping and return.
818             ProtoLog.v(WM_SHELL_TRANSITIONS, "No transition roots in %s so"
819                     + " abort", active);
820             onAbort(active);
821             return true;
822         }
823 
824         final int changeSize = info.getChanges().size();
825         boolean taskChange = false;
826         boolean transferStartingWindow = false;
827         int animBehindStartingWindow = 0;
828         boolean allOccluded = changeSize > 0;
829         for (int i = changeSize - 1; i >= 0; --i) {
830             final TransitionInfo.Change change = info.getChanges().get(i);
831             taskChange |= change.getTaskInfo() != null;
832             transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
833             if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)
834                     || change.hasAllFlags(
835                             FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
836                 animBehindStartingWindow++;
837             }
838             if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
839                 allOccluded = false;
840             } else if (change.hasAllFlags(TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION)) {
841                 // Remove the change because it should be invisible in the animation.
842                 info.getChanges().remove(i);
843                 continue;
844             }
845         }
846         // There does not need animation when:
847         // A. Transfer starting window. Apply transfer starting window directly if there is no other
848         // task change. Since this is an activity->activity situation, we can detect it by selecting
849         // transitions with changes where
850         // 1. none are tasks, and
851         // 2. one is a starting-window recipient, or all change is behind starting window.
852         if (!taskChange && (transferStartingWindow || animBehindStartingWindow == changeSize)
853                 && changeSize >= 1
854                 // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
855                 // changes are underneath another change.
856                 || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
857                 && allOccluded)) {
858             // Treat this as an abort since we are bypassing any merge logic and effectively
859             // finishing immediately.
860             ProtoLog.v(WM_SHELL_TRANSITIONS,
861                     "Non-visible anim so abort: %s", active);
862             onAbort(active);
863             return true;
864         }
865 
866         setupStartState(active.mInfo, active.mStartT, active.mFinishT);
867 
868         if (track.mReadyTransitions.size() > 1) {
869             // There are already transitions waiting in the queue, so just return.
870             return true;
871         }
872         processReadyQueue(track);
873         return true;
874     }
875 
areTracksIdle()876     private boolean areTracksIdle() {
877         for (int i = 0; i < mTracks.size(); ++i) {
878             if (!mTracks.get(i).isIdle()) return false;
879         }
880         return true;
881     }
882 
isAnimating()883     private boolean isAnimating() {
884         return !mReadyDuringSync.isEmpty() || !areTracksIdle();
885     }
886 
isIdle()887     private boolean isIdle() {
888         return mPendingTransitions.isEmpty() && !isAnimating();
889     }
890 
processReadyQueue(Track track)891     void processReadyQueue(Track track) {
892         if (track.mReadyTransitions.isEmpty()) {
893             if (track.mActiveTransition == null) {
894                 ProtoLog.v(WM_SHELL_TRANSITIONS, "Track %d became idle",
895                         mTracks.indexOf(track));
896                 if (areTracksIdle()) {
897                     if (!mReadyDuringSync.isEmpty()) {
898                         // Dispatch everything unless we hit another sync
899                         while (!mReadyDuringSync.isEmpty()) {
900                             ActiveTransition next = mReadyDuringSync.remove(0);
901                             boolean success = dispatchReady(next);
902                             // Hit a sync or sleep, so stop dispatching.
903                             if (!success) break;
904                         }
905                     } else if (mPendingTransitions.isEmpty()) {
906                         ProtoLog.v(WM_SHELL_TRANSITIONS, "All active transition "
907                                 + "animations finished");
908                         mKnownTransitions.clear();
909                         // Run all runnables from the run-when-idle queue.
910                         for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
911                             mRunWhenIdleQueue.get(i).run();
912                         }
913                         mRunWhenIdleQueue.clear();
914                     }
915                 }
916             }
917             return;
918         }
919         final ActiveTransition ready = track.mReadyTransitions.get(0);
920         if (track.mActiveTransition == null) {
921             // The normal case, just play it.
922             track.mReadyTransitions.remove(0);
923             track.mActiveTransition = ready;
924             if (ready.mAborted) {
925                 if (ready.mStartT != null) {
926                     ready.mStartT.apply();
927                 }
928                 // finish now since there's nothing to animate. Calls back into processReadyQueue
929                 onFinish(ready.mToken, null);
930                 return;
931             }
932             playTransitionWithTracing(ready);
933             // Attempt to merge any more queued-up transitions.
934             processReadyQueue(track);
935             return;
936         }
937         // An existing animation is playing, so see if we can merge.
938         final ActiveTransition playing = track.mActiveTransition;
939         final IBinder playingToken = playing.mToken;
940         final IBinder readyToken = ready.mToken;
941 
942         if (ready.mAborted) {
943             // record as merged since it is no-op. Calls back into processReadyQueue
944             onMerged(playingToken, readyToken);
945             return;
946         }
947         ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition %s ready while"
948                 + " %s is still animating. Notify the animating transition"
949                 + " in case they can be merged", ready, playing);
950         mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
951         playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT, ready.mFinishT,
952                 playing.mToken, (wct) -> onMerged(playingToken, readyToken));
953     }
954 
onMerged(@onNull IBinder playingToken, @NonNull IBinder mergedToken)955     private void onMerged(@NonNull IBinder playingToken, @NonNull IBinder mergedToken) {
956         mMainExecutor.assertCurrentThread();
957 
958         ActiveTransition playing = mKnownTransitions.get(playingToken);
959         if (playing == null) {
960             Log.e(TAG, "Merging into a non-existent transition: " + playingToken);
961             return;
962         }
963 
964         ActiveTransition merged = mKnownTransitions.get(mergedToken);
965         if (merged == null) {
966             Log.e(TAG, "Merging a non-existent transition: " + mergedToken);
967             return;
968         }
969 
970         if (playing.getTrack() != merged.getTrack()) {
971             throw new IllegalStateException("Can't merge across tracks: " + merged + " into "
972                     + playing);
973         }
974 
975         final Track track = mTracks.get(playing.getTrack());
976         ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
977                 merged, playing);
978         int readyIdx = 0;
979         if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
980             Log.e(TAG, "Merged transition out-of-order? " + merged);
981             readyIdx = track.mReadyTransitions.indexOf(merged);
982             if (readyIdx < 0) {
983                 Log.e(TAG, "Merged a transition that is no-longer queued? " + merged);
984                 return;
985             }
986         }
987         track.mReadyTransitions.remove(readyIdx);
988         if (playing.mMerged == null) {
989             playing.mMerged = new ArrayList<>();
990         }
991         playing.mMerged.add(merged);
992         // if it was aborted, then onConsumed has already been reported.
993         if (merged.mHandler != null && !merged.mAborted) {
994             merged.mHandler.onTransitionConsumed(merged.mToken, false /* abort */, merged.mFinishT);
995         }
996         for (int i = 0; i < mObservers.size(); ++i) {
997             mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken);
998         }
999         mTransitionTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
1000         // See if we should merge another transition.
1001         processReadyQueue(track);
1002     }
1003 
playTransitionWithTracing(@onNull ActiveTransition active)1004     private void playTransitionWithTracing(@NonNull ActiveTransition active) {
1005         final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER);
1006         if (useTrace) {
1007             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
1008                     "playTransition: " + transitTypeToString(active.mInfo.getType()));
1009         }
1010         playTransition(active);
1011         if (useTrace) {
1012             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1013         }
1014     }
1015 
playTransition(@onNull ActiveTransition active)1016     private void playTransition(@NonNull ActiveTransition active) {
1017         ProtoLog.v(WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
1018         final var token = active.mToken;
1019 
1020         for (int i = 0; i < mObservers.size(); ++i) {
1021             mObservers.get(i).onTransitionStarting(token);
1022         }
1023 
1024         setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
1025 
1026         // If a handler already chose to run this animation, try delegating to it first.
1027         if (active.mHandler != null) {
1028             ProtoLog.v(WM_SHELL_TRANSITIONS, " try firstHandler %s",
1029                     active.mHandler);
1030             boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
1031                     active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
1032             if (consumed) {
1033                 ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by firstHandler");
1034                 mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
1035                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
1036                     Trace.instant(TRACE_TAG_WINDOW_MANAGER,
1037                             active.mHandler.getClass().getSimpleName()
1038                                     + "#startAnimation animated "
1039                                     + transitTypeToString(active.mInfo.getType()));
1040                 }
1041                 return;
1042             }
1043         }
1044         // Otherwise give every other handler a chance
1045         active.mHandler = dispatchTransition(token, active.mInfo, active.mStartT,
1046                 active.mFinishT, (wct) -> onFinish(token, wct), active.mHandler);
1047     }
1048 
1049     /**
1050      * Gives every handler (in order) a chance to animate until one consumes the transition.
1051      * @return the handler which consumed the transition.
1052      */
dispatchTransition( @onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip )1053     public TransitionHandler dispatchTransition(
1054             @NonNull IBinder transition,
1055             @NonNull TransitionInfo info,
1056             @NonNull SurfaceControl.Transaction startT,
1057             @NonNull SurfaceControl.Transaction finishT,
1058             @NonNull TransitionFinishCallback finishCB,
1059             @Nullable TransitionHandler skip
1060     ) {
1061         for (int i = mHandlers.size() - 1; i >= 0; --i) {
1062             if (mHandlers.get(i) == skip) {
1063                 ProtoLog.v(WM_SHELL_TRANSITIONS, " skip handler %s",
1064                         mHandlers.get(i));
1065                 continue;
1066             }
1067             boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
1068                     finishCB);
1069             if (consumed) {
1070                 ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by %s",
1071                         mHandlers.get(i));
1072                 mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
1073                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
1074                     Trace.instant(TRACE_TAG_WINDOW_MANAGER,
1075                             mHandlers.get(i).getClass().getSimpleName()
1076                                     + "#startAnimation animated "
1077                                     + transitTypeToString(info.getType()));
1078                 }
1079                 return mHandlers.get(i);
1080             }
1081         }
1082         throw new IllegalStateException(
1083                 "This shouldn't happen, maybe the default handler is broken.");
1084     }
1085 
dispatchRequestWithTracing( @onNull IBinder transition, @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip)1086     private Pair<TransitionHandler, WindowContainerTransaction> dispatchRequestWithTracing(
1087             @NonNull IBinder transition, @NonNull TransitionRequestInfo request,
1088             @Nullable TransitionHandler skip) {
1089         final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER);
1090         if (useTrace) {
1091             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
1092                     "dispatchRequest: " + transitTypeToString(request.getType()));
1093         }
1094         Pair<TransitionHandler, WindowContainerTransaction> result =
1095                 dispatchRequest(transition, request, skip);
1096         if (useTrace) {
1097             if (result != null) {
1098                 Trace.instant(TRACE_TAG_WINDOW_MANAGER, result.first.getClass().getSimpleName()
1099                         + "#handleRequest handled " + transitTypeToString(request.getType()));
1100             }
1101             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
1102         }
1103         return result;
1104     }
1105 
1106     /**
1107      * Gives every handler (in order) a chance to handle request until one consumes the transition.
1108      * @return the WindowContainerTransaction given by the handler which consumed the transition.
1109      */
dispatchRequest( @onNull IBinder transition, @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip)1110     public Pair<TransitionHandler, WindowContainerTransaction> dispatchRequest(
1111             @NonNull IBinder transition, @NonNull TransitionRequestInfo request,
1112             @Nullable TransitionHandler skip) {
1113         for (int i = mHandlers.size() - 1; i >= 0; --i) {
1114             if (mHandlers.get(i) == skip) continue;
1115             WindowContainerTransaction wct = mHandlers.get(i).handleRequest(transition, request);
1116             if (wct != null) {
1117                 return new Pair<>(mHandlers.get(i), wct);
1118             }
1119         }
1120         return null;
1121     }
1122 
1123     /** Aborts a transition. This will still queue it up to maintain order. */
onAbort(ActiveTransition transition)1124     private void onAbort(ActiveTransition transition) {
1125         final Track track = mTracks.get(transition.getTrack());
1126         transition.mAborted = true;
1127 
1128         mTransitionTracer.logAborted(transition.mInfo.getDebugId());
1129 
1130         if (transition.mHandler != null) {
1131             // Notifies to clean-up the aborted transition.
1132             transition.mHandler.onTransitionConsumed(
1133                     transition.mToken, true /* aborted */, null /* finishTransaction */);
1134         }
1135 
1136         releaseSurfaces(transition.mInfo);
1137 
1138         // This still went into the queue (to maintain the correct finish ordering).
1139         if (track.mReadyTransitions.size() > 1) {
1140             // There are already transitions waiting in the queue, so just return.
1141             return;
1142         }
1143         processReadyQueue(track);
1144     }
1145 
1146     /**
1147      * Releases an info's animation-surfaces. These don't need to persist and we need to release
1148      * them asap so that SF can free memory sooner.
1149      */
releaseSurfaces(@ullable TransitionInfo info)1150     private void releaseSurfaces(@Nullable TransitionInfo info) {
1151         if (info == null) return;
1152         info.releaseAnimSurfaces();
1153     }
1154 
onFinish(IBinder token, @Nullable WindowContainerTransaction wct)1155     private void onFinish(IBinder token, @Nullable WindowContainerTransaction wct) {
1156         mMainExecutor.assertCurrentThread();
1157 
1158         final ActiveTransition active = mKnownTransitions.get(token);
1159         if (active == null) {
1160             Log.e(TAG, "Trying to finish a non-existent transition: " + token);
1161             return;
1162         }
1163 
1164         final Track track = mTracks.get(active.getTrack());
1165         if (track == null || track.mActiveTransition != active) {
1166             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
1167                     + " a handler didn't properly deal with a merge. " + active,
1168                     new RuntimeException());
1169             return;
1170         }
1171         track.mActiveTransition = null;
1172 
1173         for (int i = 0; i < mObservers.size(); ++i) {
1174             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
1175         }
1176         ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition animation finished "
1177                 + "(aborted=%b), notifying core %s", active.mAborted, active);
1178         if (active.mStartT != null) {
1179             // Applied by now, so clear immediately to remove any references. Do not set to null
1180             // yet, though, since nullness is used later to disambiguate malformed transitions.
1181             active.mStartT.clear();
1182         }
1183         // Merge all associated transactions together
1184         SurfaceControl.Transaction fullFinish = active.mFinishT;
1185         if (active.mMerged != null) {
1186             for (int iM = 0; iM < active.mMerged.size(); ++iM) {
1187                 final ActiveTransition toMerge = active.mMerged.get(iM);
1188                 // Include start. It will be a no-op if it was already applied. Otherwise, we need
1189                 // it to maintain consistent state.
1190                 if (toMerge.mStartT != null) {
1191                     if (fullFinish == null) {
1192                         fullFinish = toMerge.mStartT;
1193                     } else {
1194                         fullFinish.merge(toMerge.mStartT);
1195                     }
1196                 }
1197                 if (toMerge.mFinishT != null) {
1198                     if (fullFinish == null) {
1199                         fullFinish = toMerge.mFinishT;
1200                     } else {
1201                         fullFinish.merge(toMerge.mFinishT);
1202                     }
1203                 }
1204             }
1205         }
1206         if (fullFinish != null) {
1207             fullFinish.apply();
1208         }
1209         // Now perform all the finish callbacks (starting with the playing one and then all the
1210         // transitions merged into it).
1211         releaseSurfaces(active.mInfo);
1212         mOrganizer.finishTransition(active.mToken, wct);
1213         if (active.mMerged != null) {
1214             for (int iM = 0; iM < active.mMerged.size(); ++iM) {
1215                 ActiveTransition merged = active.mMerged.get(iM);
1216                 mOrganizer.finishTransition(merged.mToken, null /* wct */);
1217                 releaseSurfaces(merged.mInfo);
1218                 mKnownTransitions.remove(merged.mToken);
1219             }
1220             active.mMerged.clear();
1221         }
1222         mKnownTransitions.remove(token);
1223 
1224         // Now that this is done, check the ready queue for more work.
1225         processReadyQueue(track);
1226     }
1227 
requestStartTransition(@onNull IBinder transitionToken, @Nullable TransitionRequestInfo request)1228     void requestStartTransition(@NonNull IBinder transitionToken,
1229             @Nullable TransitionRequestInfo request) {
1230         ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
1231                 request.getDebugId(), transitionToken, request);
1232         if (mKnownTransitions.containsKey(transitionToken)) {
1233             throw new RuntimeException("Transition already started " + transitionToken);
1234         }
1235         final ActiveTransition active = new ActiveTransition(transitionToken);
1236         mKnownTransitions.put(transitionToken, active);
1237         WindowContainerTransaction wct = null;
1238 
1239         // If we have sleep, we use a special handler and we try to finish everything ASAP.
1240         if (request.getType() == TRANSIT_SLEEP) {
1241             mSleepHandler.handleRequest(transitionToken, request);
1242             active.mHandler = mSleepHandler;
1243         } else {
1244             Pair<TransitionHandler, WindowContainerTransaction> requestResult =
1245                     dispatchRequestWithTracing(transitionToken, request, /* skip= */ null);
1246             if (requestResult != null) {
1247                 active.mHandler = requestResult.first;
1248                 wct = requestResult.second;
1249                 ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition (#%d): request handled by %s",
1250                         request.getDebugId(), active.mHandler.getClass().getSimpleName());
1251             }
1252             if (request.getDisplayChange() != null) {
1253                 TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
1254                 if (change.getStartRotation() != change.getEndRotation()
1255                         || (change.getStartAbsBounds() != null
1256                         && !change.getStartAbsBounds().equals(change.getEndAbsBounds()))) {
1257                     // Is a display change, so dispatch to all displayChange listeners
1258                     if (wct == null) {
1259                         wct = new WindowContainerTransaction();
1260                     }
1261                     mDisplayController.onDisplayChangeRequested(wct, change.getDisplayId(),
1262                             change.getStartAbsBounds(), change.getEndAbsBounds(),
1263                             change.getStartRotation(), change.getEndRotation());
1264                 }
1265             }
1266         }
1267         final boolean isOccludingKeyguard = request.getType() == TRANSIT_KEYGUARD_OCCLUDE
1268                 || ((request.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0);
1269         if (isOccludingKeyguard && request.getTriggerTask() != null
1270                 && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
1271             // This freeform task is on top of keyguard, so its windowing mode should be changed to
1272             // fullscreen.
1273             if (wct == null) {
1274                 wct = new WindowContainerTransaction();
1275             }
1276             wct.setWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_FULLSCREEN);
1277             wct.setBounds(request.getTriggerTask().token, null);
1278         }
1279         mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
1280         // Currently, WMCore only does one transition at a time. If it makes a requestStart, it
1281         // is already collecting that transition on core-side, so it will be the next one to
1282         // become ready. There may already be pending transitions added as part of direct
1283         // `startNewTransition` but if we have a request now, it means WM created the request
1284         // transition before it acknowledged any of the pending `startNew` transitions. So, insert
1285         // it at the front.
1286         mPendingTransitions.add(0, active);
1287     }
1288 
1289     /**
1290      * Start a new transition directly.
1291      * @param handler if null, the transition will be dispatched to the registered set of transition
1292      *                handlers to be handled
1293      */
startTransition(@indowManager.TransitionType int type, @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler)1294     public IBinder startTransition(@WindowManager.TransitionType int type,
1295             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
1296         ProtoLog.v(WM_SHELL_TRANSITIONS, "Directly starting a new transition "
1297                 + "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
1298         if (DEBUG_START_TRANSITION) {
1299             Log.d(TAG, "startTransition: type=" + transitTypeToString(type)
1300                     + " wct=" + wct + " handler=" + handler.getClass().getName(), new Throwable());
1301         }
1302         final ActiveTransition active =
1303                 new ActiveTransition(mOrganizer.startNewTransition(type, wct));
1304         active.mHandler = handler;
1305         mKnownTransitions.put(active.mToken, active);
1306         mPendingTransitions.add(active);
1307         return active.mToken;
1308     }
1309 
1310     /**
1311      * Checks whether a handler exists capable of taking over the given transition, and returns it.
1312      * Otherwise it returns null.
1313      */
1314     @Nullable
getHandlerForTakeover( @onNull IBinder transition, @NonNull TransitionInfo info)1315     public TransitionHandler getHandlerForTakeover(
1316             @NonNull IBinder transition, @NonNull TransitionInfo info) {
1317         if (!returnAnimationFrameworkLongLived()) {
1318             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
1319                     "Trying to get a handler for takeover but the flag is disabled");
1320             return null;
1321         }
1322 
1323         for (TransitionHandler handler : mHandlers) {
1324             TransitionHandler candidate = handler.getHandlerForTakeover(transition, info);
1325             if (candidate != null) {
1326                 return candidate;
1327             }
1328         }
1329 
1330         return null;
1331     }
1332 
1333     /**
1334      * Finish running animations (almost) immediately when a SLEEP transition comes in. We use this
1335      * as both a way to reduce unnecessary work (animations not visible while screen off) and as a
1336      * failsafe to unblock "stuck" animations (in particular remote animations).
1337      *
1338      * This works by "merging" the sleep transition into the currently-playing transition (even if
1339      * its out-of-order) -- turning SLEEP into a signal. If the playing transition doesn't finish
1340      * within `SYNC_ALLOWANCE_MS` from this merge attempt, this will then finish it directly (and
1341      * send an abort/consumed message).
1342      *
1343      * This is then repeated until there are no more pending sleep transitions.
1344      *
1345      * @param reason The token for the SLEEP transition that triggered this round of finishes.
1346      *               We will continue looping round finishing transitions until this is ready.
1347      * @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
1348      *                    signal to -- so it will be force-finished if it's still running.
1349      */
finishForSync(IBinder reason, int trackIdx, @Nullable ActiveTransition forceFinish)1350     private void finishForSync(IBinder reason,
1351             int trackIdx, @Nullable ActiveTransition forceFinish) {
1352         if (!mKnownTransitions.containsKey(reason)) {
1353             Log.d(TAG, "finishForSleep: already played sync transition " + reason);
1354             return;
1355         }
1356         final Track track = mTracks.get(trackIdx);
1357         if (forceFinish != null) {
1358             final Track trk = mTracks.get(forceFinish.getTrack());
1359             if (trk != track) {
1360                 Log.e(TAG, "finishForSleep: mismatched Tracks between forceFinish and logic "
1361                         + forceFinish.getTrack() + " vs " + trackIdx);
1362             }
1363             if (trk.mActiveTransition == forceFinish) {
1364                 Log.e(TAG, "Forcing transition to finish due to sync timeout: " + forceFinish);
1365                 forceFinish.mAborted = true;
1366                 // Last notify of it being consumed. Note: mHandler should never be null,
1367                 // but check just to be safe.
1368                 if (forceFinish.mHandler != null) {
1369                     forceFinish.mHandler.onTransitionConsumed(
1370                             forceFinish.mToken, true /* aborted */, null /* finishTransaction */);
1371                 }
1372                 onFinish(forceFinish.mToken, null);
1373             }
1374         }
1375         if (track.isIdle() || mReadyDuringSync.isEmpty()) {
1376             // Done finishing things.
1377             return;
1378         }
1379         final SurfaceControl.Transaction dummyT = new SurfaceControl.Transaction();
1380         final TransitionInfo dummyInfo = new TransitionInfo(TRANSIT_SLEEP, 0 /* flags */);
1381         while (track.mActiveTransition != null && !mReadyDuringSync.isEmpty()) {
1382             final ActiveTransition playing = track.mActiveTransition;
1383             final ActiveTransition nextSync = mReadyDuringSync.get(0);
1384             if (!nextSync.isSync()) {
1385                 Log.e(TAG, "Somehow blocked on a non-sync transition? " + nextSync);
1386             }
1387             // Attempt to merge a SLEEP info to signal that the playing transition needs to
1388             // fast-forward.
1389             ProtoLog.v(WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
1390                     + " into %s via a SLEEP proxy", nextSync, playing);
1391             playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
1392                     playing.mToken, (wct) -> {});
1393             // it's possible to complete immediately. If that happens, just repeat the signal
1394             // loop until we either finish everything or start playing an animation that isn't
1395             // finishing immediately.
1396             if (track.mActiveTransition == playing) {
1397                 if (!mDisableForceSync) {
1398                     // Give it a short amount of time to process it before forcing.
1399                     mMainExecutor.executeDelayed(
1400                             () -> finishForSync(reason, trackIdx, playing), SYNC_ALLOWANCE_MS);
1401                 }
1402                 break;
1403             }
1404         }
1405     }
1406 
getHomeTaskOverlayContainer()1407     private SurfaceControl getHomeTaskOverlayContainer() {
1408         return mOrganizer.getHomeTaskOverlayContainer();
1409     }
1410 
1411     /**
1412      * Interface for a callback that must be called after a TransitionHandler finishes playing an
1413      * animation.
1414      */
1415     public interface TransitionFinishCallback {
1416         /**
1417          * This must be called on the main thread when a transition finishes playing an animation.
1418          * The transition must not touch the surfaces after this has been called.
1419          *
1420          * @param wct A WindowContainerTransaction to run along with the transition clean-up.
1421          */
onTransitionFinished(@ullable WindowContainerTransaction wct)1422         void onTransitionFinished(@Nullable WindowContainerTransaction wct);
1423     }
1424 
1425     /**
1426      * Interface for something which can handle a subset of transitions.
1427      */
1428     public interface TransitionHandler {
1429         /**
1430          * Starts a transition animation. This is always called if handleRequest returned non-null
1431          * for a particular transition. Otherwise, it is only called if no other handler before
1432          * it handled the transition.
1433          * @param startTransaction the transaction given to the handler to be applied before the
1434          *                         transition animation. Note the handler is expected to call on
1435          *                         {@link SurfaceControl.Transaction#apply()} for startTransaction.
1436          * @param finishTransaction the transaction given to the handler to be applied after the
1437          *                       transition animation. Unlike startTransaction, the handler is NOT
1438          *                       expected to apply this transaction. The Transition system will
1439          *                       apply it when finishCallback is called. If additional transitions
1440          *                       are merged, then the finish transactions for those transitions
1441          *                       will be applied after this transaction.
1442          * @param finishCallback Call this when finished. This MUST be called on main thread.
1443          * @return true if transition was handled, false if not (falls-back to default).
1444          */
startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback)1445         boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
1446                 @NonNull SurfaceControl.Transaction startTransaction,
1447                 @NonNull SurfaceControl.Transaction finishTransaction,
1448                 @NonNull TransitionFinishCallback finishCallback);
1449 
1450         /**
1451          * See {@link #mergeAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, SurfaceControl.Transaction, IBinder, TransitionFinishCallback)}
1452          *
1453          * This deprecated method header is provided until downstream implementation can migrate to
1454          * the call that takes both start & finish transactions.
1455          */
1456         @Deprecated
mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback)1457         default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
1458                 @NonNull SurfaceControl.Transaction startTransaction,
1459                 @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) { }
1460 
1461         /**
1462          * Attempts to merge a different transition's animation into an animation that this handler
1463          * is currently playing. If a merge is not possible/supported, this should be a no-op.
1464          *
1465          * This gets called if another transition becomes ready while this handler is still playing
1466          * an animation. This is called regardless of whether this handler claims to support that
1467          * particular transition or not.
1468          *
1469          * When this happens, there are 2 options:
1470          *  1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
1471          *  2. Merge the incoming transition into this one. The implementation is up to this
1472          *     handler. To indicate that this handler has "consumed" the merge transition, it
1473          *     must call the finishCallback immediately, or at-least before the original
1474          *     transition's finishCallback is called.
1475          *
1476          * @param transition This is the transition that wants to be merged.
1477          * @param info Information about what is changing in the transition.
1478          * @param startTransaction The start transaction containing surface changes that resulted
1479          *                         from the incoming transition. This should be applied by this
1480          *                         active handler only if it chooses to merge the transition.
1481          * @param finishTransaction The finish transaction for the incoming transition. Unlike
1482          *                          startTransaction, the handler is NOT expected to apply this
1483          *                          transaction. If the transition is merged, the Transition system
1484          *                          will apply after finishCallback is called following the finish
1485          *                          transaction provided in `#startAnimation()`.
1486          * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
1487          *                    one this handler is currently already animating).
1488          * @param finishCallback Call this if merged. This MUST be called on main thread.
1489          */
mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback)1490         default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
1491                 @NonNull SurfaceControl.Transaction startTransaction,
1492                 @NonNull SurfaceControl.Transaction finishTransaction,
1493                 @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) {
1494             // Call the legacy implementation by default
1495             mergeAnimation(transition, info, startTransaction, mergeTarget, finishCallback);
1496         }
1497 
1498         /**
1499          * Checks whether this handler is capable of taking over a transition matching `info`.
1500          * {@link TransitionHandler#takeOverAnimation(IBinder, TransitionInfo,
1501          * SurfaceControl.Transaction, TransitionFinishCallback, WindowAnimationState[])} is
1502          * guaranteed to succeed if called on the handler returned by this method.
1503          *
1504          * Note that the handler returned by this method can either be itself, or a different one
1505          * selected by this handler to take care of the transition on its behalf.
1506          *
1507          * @param transition The transition that should be taken over.
1508          * @param info Information about the transition to be taken over.
1509          * @return A handler capable of taking over a matching transition, or null.
1510          */
1511         @Nullable
getHandlerForTakeover( @onNull IBinder transition, @NonNull TransitionInfo info)1512         default TransitionHandler getHandlerForTakeover(
1513                 @NonNull IBinder transition, @NonNull TransitionInfo info) {
1514             return null;
1515         }
1516 
1517         /**
1518          * Attempt to take over a running transition. This must succeed if this handler was returned
1519          * by {@link TransitionHandler#getHandlerForTakeover(IBinder, TransitionInfo)}.
1520          *
1521          * @param transition The transition that should be taken over.
1522          * @param info Information about the what is changing in the transition.
1523          * @param transaction Contains surface changes that resulted from the transition. Any
1524          *                    additional changes should be added to this transaction and committed
1525          *                    inside this method.
1526          * @param finishCallback Call this at the end of the animation, if the take-over succeeds.
1527          *                       Note that this will be called instead of the callback originally
1528          *                       passed to startAnimation(), so the caller should make sure all
1529          *                       necessary cleanup happens here. This MUST be called on main thread.
1530          * @param states The animation states of the transition's window at the time this method was
1531          *               called.
1532          * @return true if the transition was taken over, false if not.
1533          */
takeOverAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction transaction, @NonNull TransitionFinishCallback finishCallback, @NonNull WindowAnimationState[] states)1534         default boolean takeOverAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
1535                 @NonNull SurfaceControl.Transaction transaction,
1536                 @NonNull TransitionFinishCallback finishCallback,
1537                 @NonNull WindowAnimationState[] states) {
1538             return false;
1539         }
1540 
1541         /**
1542          * Potentially handles a startTransition request.
1543          *
1544          * @param transition The transition whose start is being requested.
1545          * @param request Information about what is requested.
1546          * @return WCT to apply with transition-start or null. If a WCT is returned here, this
1547          *         handler will be the first in line to animate.
1548          */
1549         @Nullable
handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)1550         WindowContainerTransaction handleRequest(@NonNull IBinder transition,
1551                 @NonNull TransitionRequestInfo request);
1552 
1553         /**
1554          * Called when a transition which was already "claimed" by this handler has been merged
1555          * into another animation or has been aborted. Gives this handler a chance to clean-up any
1556          * expectations.
1557          *
1558          * @param transition The transition been consumed.
1559          * @param aborted Whether the transition is aborted or not.
1560          * @param finishTransaction The transaction to be applied after the transition animated.
1561          */
onTransitionConsumed(@onNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishTransaction)1562         default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
1563                 @Nullable SurfaceControl.Transaction finishTransaction) { }
1564 
1565         /**
1566          * Sets transition animation scale settings value to handler.
1567          *
1568          * @param scale The setting value of transition animation scale.
1569          */
setAnimScaleSetting(float scale)1570         default void setAnimScaleSetting(float scale) {}
1571     }
1572 
1573     /**
1574      * Interface for something that needs to know the lifecycle of some transitions, but never
1575      * handles any transition by itself.
1576      */
1577     public interface TransitionObserver {
1578         /**
1579          * Called when the transition is ready to play. It may later be merged into other
1580          * transitions. Note this doesn't mean this transition will be played anytime soon.
1581          *
1582          * @param transition the unique token of this transition
1583          * @param startTransaction the transaction given to the handler to be applied before the
1584          *                         transition animation. This will be applied when the transition
1585          *                         handler that handles this transition starts the transition.
1586          * @param finishTransaction the transaction given to the handler to be applied after the
1587          *                          transition animation. The Transition system will apply it when
1588          *                          finishCallback is called by the transition handler.
1589          */
onTransitionReady(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction)1590         default void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
1591                 @NonNull SurfaceControl.Transaction startTransaction,
1592                 @NonNull SurfaceControl.Transaction finishTransaction) {}
1593 
1594         /**
1595          * Called when the transition is starting to play. It isn't called for merged transitions.
1596          *
1597          * @param transition the unique token of this transition
1598          */
onTransitionStarting(@onNull IBinder transition)1599         default void onTransitionStarting(@NonNull IBinder transition) {}
1600 
1601         /**
1602          * Called when a transition is merged into another transition. There won't be any following
1603          * lifecycle calls for the merged transition.
1604          *
1605          * @param merged the unique token of the transition that's merged to another one
1606          * @param playing the unique token of the transition that accepts the merge
1607          */
onTransitionMerged(@onNull IBinder merged, @NonNull IBinder playing)1608         default void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {}
1609 
1610         /**
1611          * Called when the transition is finished. This isn't called for merged transitions.
1612          *
1613          * @param transition the unique token of this transition
1614          * @param aborted {@code true} if this transition is aborted; {@code false} otherwise.
1615          */
onTransitionFinished(@onNull IBinder transition, boolean aborted)1616         default void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {}
1617     }
1618 
1619     @BinderThread
1620     private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
1621         @Override
onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo, SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)1622         public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
1623                 SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
1624                 throws RemoteException {
1625             ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
1626                     t.getId());
1627             mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
1628                     iBinder, transitionInfo, t, finishT));
1629         }
1630 
1631         @Override
requestStartTransition(IBinder iBinder, TransitionRequestInfo request)1632         public void requestStartTransition(IBinder iBinder,
1633                 TransitionRequestInfo request) throws RemoteException {
1634             mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
1635         }
1636     }
1637 
1638     /**
1639      * The interface for calls from outside the Shell, within the host process.
1640      */
1641     @ExternalThread
1642     private class ShellTransitionImpl implements ShellTransitions {
1643         @Override
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)1644         public void registerRemote(@NonNull TransitionFilter filter,
1645                 @NonNull RemoteTransition remoteTransition) {
1646             mMainExecutor.execute(
1647                     () -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
1648         }
1649 
1650         @Override
registerRemoteForTakeover(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)1651         public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
1652                 @NonNull RemoteTransition remoteTransition) {
1653             mMainExecutor.execute(() -> mRemoteTransitionHandler.addFilteredForTakeover(
1654                     filter, remoteTransition));
1655         }
1656 
1657         @Override
unregisterRemote(@onNull RemoteTransition remoteTransition)1658         public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
1659             mMainExecutor.execute(
1660                     () -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
1661         }
1662 
1663         @Override
setFocusTransitionListener(FocusTransitionListener listener, Executor executor)1664         public void setFocusTransitionListener(FocusTransitionListener listener,
1665                 Executor executor) {
1666             mMainExecutor.execute(() ->
1667                     mFocusTransitionObserver.setLocalFocusTransitionListener(listener, executor));
1668 
1669         }
1670 
1671         @Override
unsetFocusTransitionListener(FocusTransitionListener listener)1672         public void unsetFocusTransitionListener(FocusTransitionListener listener) {
1673             mMainExecutor.execute(() ->
1674                     mFocusTransitionObserver.unsetLocalFocusTransitionListener(listener));
1675 
1676         }
1677     }
1678 
1679     /**
1680      * The interface for calls from outside the host process.
1681      */
1682     @BinderThread
1683     private static class IShellTransitionsImpl extends IShellTransitions.Stub
1684             implements ExternalInterfaceBinder {
1685         private Transitions mTransitions;
1686 
IShellTransitionsImpl(Transitions transitions)1687         IShellTransitionsImpl(Transitions transitions) {
1688             mTransitions = transitions;
1689         }
1690 
1691         /**
1692          * Invalidates this instance, preventing future calls from updating the controller.
1693          */
1694         @Override
invalidate()1695         public void invalidate() {
1696             mTransitions.mHomeTransitionObserver.invalidate(mTransitions);
1697             mTransitions = null;
1698         }
1699 
1700         @Override
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)1701         public void registerRemote(@NonNull TransitionFilter filter,
1702                 @NonNull RemoteTransition remoteTransition) {
1703             executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
1704                     (transitions) -> transitions.mRemoteTransitionHandler.addFiltered(
1705                             filter, remoteTransition));
1706         }
1707 
1708         @Override
registerRemoteForTakeover(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)1709         public void registerRemoteForTakeover(@NonNull TransitionFilter filter,
1710                 @NonNull RemoteTransition remoteTransition) {
1711             executeRemoteCallWithTaskPermission(mTransitions, "registerRemoteForTakeover",
1712                     (transitions) -> transitions.mRemoteTransitionHandler.addFilteredForTakeover(
1713                             filter, remoteTransition));
1714         }
1715 
1716         @Override
unregisterRemote(@onNull RemoteTransition remoteTransition)1717         public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
1718             executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
1719                     (transitions) ->
1720                             transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition));
1721         }
1722 
1723         @Override
getShellApplyToken()1724         public IBinder getShellApplyToken() {
1725             return SurfaceControl.Transaction.getDefaultApplyToken();
1726         }
1727 
1728         @Override
setHomeTransitionListener(IHomeTransitionListener listener)1729         public void setHomeTransitionListener(IHomeTransitionListener listener) {
1730             executeRemoteCallWithTaskPermission(mTransitions, "setHomeTransitionListener",
1731                     (transitions) -> {
1732                         transitions.mHomeTransitionObserver.setHomeTransitionListener(transitions,
1733                                 listener);
1734                     });
1735         }
1736 
1737         @Override
setFocusTransitionListener(IFocusTransitionListener listener)1738         public void setFocusTransitionListener(IFocusTransitionListener listener) {
1739             executeRemoteCallWithTaskPermission(mTransitions, "setFocusTransitionListener",
1740                     (transitions) -> {
1741                         transitions.mFocusTransitionObserver.setRemoteFocusTransitionListener(
1742                                 transitions, listener);
1743                     });
1744         }
1745 
1746         @Override
getHomeTaskOverlayContainer()1747         public SurfaceControl getHomeTaskOverlayContainer() {
1748             SurfaceControl[] result = new SurfaceControl[1];
1749             executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer",
1750                     (controller) -> {
1751                         result[0] = controller.getHomeTaskOverlayContainer();
1752                     }, true /* blocking */);
1753             // Return a copy as writing to parcel releases the original surface
1754             return new SurfaceControl(result[0], "Transitions.HomeOverlay");
1755         }
1756     }
1757 
1758     private class SettingsObserver extends ContentObserver {
1759 
SettingsObserver()1760         SettingsObserver() {
1761             super(null);
1762         }
1763 
1764         @Override
onChange(boolean selfChange)1765         public void onChange(boolean selfChange) {
1766             super.onChange(selfChange);
1767             mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
1768 
1769             mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
1770         }
1771     }
1772 
1773     @Override
onShellCommand(String[] args, PrintWriter pw)1774     public boolean onShellCommand(String[] args, PrintWriter pw) {
1775         if (args.length == 0) {
1776             printShellCommandHelp(pw, "");
1777             return false;
1778         }
1779         switch (args[0]) {
1780             case "tracing": {
1781                 if (!android.tracing.Flags.perfettoTransitionTracing()) {
1782                     ((LegacyTransitionTracer) mTransitionTracer)
1783                             .onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
1784                 } else {
1785                     pw.println("Command not supported. Use the Perfetto command instead to start "
1786                             + "and stop this trace instead.");
1787                     return false;
1788                 }
1789                 return true;
1790             }
1791             default: {
1792                 pw.println("Invalid command: " + args[0]);
1793                 printShellCommandHelp(pw, "");
1794                 return false;
1795             }
1796         }
1797     }
1798 
1799     @Override
printShellCommandHelp(PrintWriter pw, String prefix)1800     public void printShellCommandHelp(PrintWriter pw, String prefix) {
1801         if (!android.tracing.Flags.perfettoTransitionTracing()) {
1802             pw.println(prefix + "tracing");
1803             ((LegacyTransitionTracer) mTransitionTracer).printShellCommandHelp(pw, prefix + "  ");
1804         }
1805     }
1806 
dump(@onNull PrintWriter pw, String prefix)1807     private void dump(@NonNull PrintWriter pw, String prefix) {
1808         pw.println(prefix + TAG);
1809 
1810         final String innerPrefix = prefix + "  ";
1811         pw.println(prefix + "Handlers (ordered by priority):");
1812         for (int i = mHandlers.size() - 1; i >= 0; i--) {
1813             final TransitionHandler handler = mHandlers.get(i);
1814             pw.print(innerPrefix);
1815             pw.print(handler.getClass().getSimpleName());
1816             pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
1817         }
1818 
1819         mRemoteTransitionHandler.dump(pw, prefix);
1820 
1821         pw.println(prefix + "Observers:");
1822         for (TransitionObserver observer : mObservers) {
1823             pw.print(innerPrefix);
1824             pw.println(observer.getClass().getSimpleName());
1825         }
1826 
1827         pw.println(prefix + "Pending Transitions:");
1828         for (ActiveTransition transition : mPendingTransitions) {
1829             pw.print(innerPrefix + "token=");
1830             pw.println(transition.mToken);
1831             pw.print(innerPrefix + "id=");
1832             pw.println(transition.mInfo != null
1833                     ? transition.mInfo.getDebugId()
1834                     : -1);
1835             pw.print(innerPrefix + "handler=");
1836             pw.println(transition.mHandler != null
1837                     ? transition.mHandler.getClass().getSimpleName()
1838                     : null);
1839         }
1840         if (mPendingTransitions.isEmpty()) {
1841             pw.println(innerPrefix + "none");
1842         }
1843 
1844         pw.println(prefix + "Ready-during-sync Transitions:");
1845         for (ActiveTransition transition : mReadyDuringSync) {
1846             pw.print(innerPrefix + "token=");
1847             pw.println(transition.mToken);
1848             pw.print(innerPrefix + "id=");
1849             pw.println(transition.mInfo != null
1850                     ? transition.mInfo.getDebugId()
1851                     : -1);
1852             pw.print(innerPrefix + "handler=");
1853             pw.println(transition.mHandler != null
1854                     ? transition.mHandler.getClass().getSimpleName()
1855                     : null);
1856         }
1857         if (mReadyDuringSync.isEmpty()) {
1858             pw.println(innerPrefix + "none");
1859         }
1860 
1861         pw.println(prefix + "Tracks:");
1862         for (int i = 0; i < mTracks.size(); i++) {
1863             final ActiveTransition transition = mTracks.get(i).mActiveTransition;
1864             pw.println(innerPrefix + "Track #" + i);
1865             pw.print(innerPrefix + "active=");
1866             pw.println(transition);
1867             if (transition != null) {
1868                 pw.print(innerPrefix + "hander=");
1869                 pw.println(transition.mHandler);
1870             }
1871         }
1872     }
1873 
1874     /**
1875      * Like WindowManager#transitTypeToString(), but also covers known custom transition types as
1876      * well.
1877      */
transitTypeToString(int transitType)1878     public static String transitTypeToString(int transitType) {
1879         if (transitType < TRANSIT_FIRST_CUSTOM) {
1880             return WindowManager.transitTypeToString(transitType);
1881         }
1882 
1883         String typeStr = switch (transitType) {
1884             case TRANSIT_EXIT_PIP -> "EXIT_PIP";
1885             case TRANSIT_EXIT_PIP_TO_SPLIT -> "EXIT_PIP_TO_SPLIT";
1886             case TRANSIT_REMOVE_PIP -> "REMOVE_PIP";
1887             case TRANSIT_SPLIT_SCREEN_PAIR_OPEN -> "SPLIT_SCREEN_PAIR_OPEN";
1888             case TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE -> "SPLIT_SCREEN_OPEN_TO_SIDE";
1889             case TRANSIT_SPLIT_DISMISS_SNAP -> "SPLIT_DISMISS_SNAP";
1890             case TRANSIT_SPLIT_DISMISS -> "SPLIT_DISMISS";
1891             case TRANSIT_MAXIMIZE -> "MAXIMIZE";
1892             case TRANSIT_RESTORE_FROM_MAXIMIZE -> "RESTORE_FROM_MAXIMIZE";
1893             case TRANSIT_RESIZE_PIP -> "RESIZE_PIP";
1894             case TRANSIT_TASK_FRAGMENT_DRAG_RESIZE -> "TASK_FRAGMENT_DRAG_RESIZE";
1895             case TRANSIT_SPLIT_PASSTHROUGH -> "SPLIT_PASSTHROUGH";
1896             case TRANSIT_CLEANUP_PIP_EXIT -> "CLEANUP_PIP_EXIT";
1897             case TRANSIT_MINIMIZE -> "MINIMIZE";
1898             case TRANSIT_START_RECENTS_TRANSITION -> "START_RECENTS_TRANSITION";
1899             case TRANSIT_END_RECENTS_TRANSITION -> "END_RECENTS_TRANSITION";
1900             case TRANSIT_CONVERT_TO_BUBBLE -> "CONVERT_TO_BUBBLE";
1901             default -> "";
1902         };
1903         if (typeStr.isEmpty()) {
1904             typeStr = DesktopModeTransitionTypes.transitTypeToString(transitType);
1905         }
1906         return typeStr + "(FIRST_CUSTOM+" + (transitType - TRANSIT_FIRST_CUSTOM) + ")";
1907     }
1908 
getShellTransitEnabled()1909     private static boolean getShellTransitEnabled() {
1910         try {
1911             if (AppGlobals.getPackageManager().hasSystemFeature(
1912                     PackageManager.FEATURE_AUTOMOTIVE, 0)) {
1913                 return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
1914             }
1915         } catch (RemoteException re) {
1916             Log.w(TAG, "Error getting system features");
1917         }
1918         return true;
1919     }
1920 }
1921