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