• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.transition;
18 
19 import static android.view.WindowManager.TRANSIT_CHANGE;
20 import static android.view.WindowManager.TRANSIT_CLOSE;
21 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
22 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
23 import static android.view.WindowManager.TRANSIT_OPEN;
24 import static android.view.WindowManager.TRANSIT_TO_BACK;
25 import static android.view.WindowManager.TRANSIT_TO_FRONT;
26 import static android.view.WindowManager.fixScale;
27 import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
28 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
29 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
30 
31 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
32 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
33 
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.app.ActivityTaskManager;
37 import android.app.IApplicationThread;
38 import android.content.ContentResolver;
39 import android.content.Context;
40 import android.database.ContentObserver;
41 import android.os.Handler;
42 import android.os.IBinder;
43 import android.os.RemoteException;
44 import android.os.SystemProperties;
45 import android.provider.Settings;
46 import android.util.Log;
47 import android.view.SurfaceControl;
48 import android.view.WindowManager;
49 import android.window.ITransitionPlayer;
50 import android.window.RemoteTransition;
51 import android.window.TransitionFilter;
52 import android.window.TransitionInfo;
53 import android.window.TransitionMetrics;
54 import android.window.TransitionRequestInfo;
55 import android.window.WindowContainerTransaction;
56 import android.window.WindowContainerTransactionCallback;
57 import android.window.WindowOrganizer;
58 
59 import androidx.annotation.BinderThread;
60 
61 import com.android.internal.R;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.protolog.common.ProtoLog;
64 import com.android.wm.shell.common.DisplayController;
65 import com.android.wm.shell.common.ExternalInterfaceBinder;
66 import com.android.wm.shell.common.RemoteCallable;
67 import com.android.wm.shell.common.ShellExecutor;
68 import com.android.wm.shell.common.TransactionPool;
69 import com.android.wm.shell.common.annotations.ExternalThread;
70 import com.android.wm.shell.protolog.ShellProtoLogGroup;
71 import com.android.wm.shell.sysui.ShellController;
72 import com.android.wm.shell.sysui.ShellInit;
73 
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 
77 /** Plays transition animations */
78 public class Transitions implements RemoteCallable<Transitions> {
79     static final String TAG = "ShellTransitions";
80 
81     /** Set to {@code true} to enable shell transitions. */
82     public static final boolean ENABLE_SHELL_TRANSITIONS =
83             SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
84     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
85             && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
86 
87     /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
88     public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
89 
90     public static final int TRANSIT_EXIT_PIP_TO_SPLIT =  TRANSIT_FIRST_CUSTOM + 2;
91 
92     /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
93     public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
94 
95     /** Transition type for launching 2 tasks simultaneously. */
96     public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
97 
98     /** Transition type for entering split by opening an app into side-stage. */
99     public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
100 
101     /** Transition type for dismissing split-screen via dragging the divider off the screen. */
102     public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
103 
104     /** Transition type for dismissing split-screen. */
105     public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
106 
107     /** Transition type for freeform to maximize transition. */
108     public static final int TRANSIT_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 8;
109 
110     /** Transition type for maximize to freeform transition. */
111     public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
112 
113     private final WindowOrganizer mOrganizer;
114     private final Context mContext;
115     private final ShellExecutor mMainExecutor;
116     private final ShellExecutor mAnimExecutor;
117     private final TransitionPlayerImpl mPlayerImpl;
118     private final DefaultTransitionHandler mDefaultTransitionHandler;
119     private final RemoteTransitionHandler mRemoteTransitionHandler;
120     private final DisplayController mDisplayController;
121     private final ShellController mShellController;
122     private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
123 
124     private boolean mIsRegistered = false;
125 
126     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
127     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
128 
129     private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
130 
131     /** List of {@link Runnable} instances to run when the last active transition has finished.  */
132     private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
133 
134     private float mTransitionAnimationScaleSetting = 1.0f;
135 
136     private static final class ActiveTransition {
137         IBinder mToken;
138         TransitionHandler mHandler;
139         boolean mMerged;
140         boolean mAborted;
141         TransitionInfo mInfo;
142         SurfaceControl.Transaction mStartT;
143         SurfaceControl.Transaction mFinishT;
144     }
145 
146     /** Keeps track of currently playing transitions in the order of receipt. */
147     private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
148 
Transitions(@onNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor)149     public Transitions(@NonNull Context context,
150             @NonNull ShellInit shellInit,
151             @NonNull ShellController shellController,
152             @NonNull WindowOrganizer organizer,
153             @NonNull TransactionPool pool,
154             @NonNull DisplayController displayController,
155             @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
156             @NonNull ShellExecutor animExecutor) {
157         mOrganizer = organizer;
158         mContext = context;
159         mMainExecutor = mainExecutor;
160         mAnimExecutor = animExecutor;
161         mDisplayController = displayController;
162         mPlayerImpl = new TransitionPlayerImpl();
163         mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
164                 displayController, pool, mainExecutor, mainHandler, animExecutor);
165         mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
166         mShellController = shellController;
167         // The very last handler (0 in the list) should be the default one.
168         mHandlers.add(mDefaultTransitionHandler);
169         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
170         // Next lowest priority is remote transitions.
171         mHandlers.add(mRemoteTransitionHandler);
172         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
173         shellInit.addInitCallback(this::onInit, this);
174     }
175 
onInit()176     private void onInit() {
177         mShellController.addExternalInterface(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
178                 this::createExternalInterface, this);
179 
180         ContentResolver resolver = mContext.getContentResolver();
181         mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
182         dispatchAnimScaleSetting(mTransitionAnimationScaleSetting);
183 
184         resolver.registerContentObserver(
185                 Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
186                 new SettingsObserver());
187 
188         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
189             mIsRegistered = true;
190             // Register this transition handler with Core
191             try {
192                 mOrganizer.registerTransitionPlayer(mPlayerImpl);
193             } catch (RuntimeException e) {
194                 mIsRegistered = false;
195                 throw e;
196             }
197             // Pre-load the instance.
198             TransitionMetrics.getInstance();
199         }
200     }
201 
isRegistered()202     public boolean isRegistered() {
203         return mIsRegistered;
204     }
205 
getTransitionAnimationScaleSetting()206     private float getTransitionAnimationScaleSetting() {
207         return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
208                 Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
209                                 R.dimen.config_appTransitionAnimationDurationScaleDefault)));
210     }
211 
asRemoteTransitions()212     public ShellTransitions asRemoteTransitions() {
213         return mImpl;
214     }
215 
createExternalInterface()216     private ExternalInterfaceBinder createExternalInterface() {
217         return new IShellTransitionsImpl(this);
218     }
219 
220     @Override
getContext()221     public Context getContext() {
222         return mContext;
223     }
224 
225     @Override
getRemoteCallExecutor()226     public ShellExecutor getRemoteCallExecutor() {
227         return mMainExecutor;
228     }
229 
dispatchAnimScaleSetting(float scale)230     private void dispatchAnimScaleSetting(float scale) {
231         for (int i = mHandlers.size() - 1; i >= 0; --i) {
232             mHandlers.get(i).setAnimScaleSetting(scale);
233         }
234     }
235 
236     /**
237      * Adds a handler candidate.
238      * @see TransitionHandler
239      */
addHandler(@onNull TransitionHandler handler)240     public void addHandler(@NonNull TransitionHandler handler) {
241         if (mHandlers.isEmpty()) {
242             throw new RuntimeException("Unexpected handler added prior to initialization, please "
243                     + "use ShellInit callbacks to ensure proper ordering");
244         }
245         mHandlers.add(handler);
246         // Set initial scale settings.
247         handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
248         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
249                 handler.getClass().getSimpleName());
250     }
251 
getMainExecutor()252     public ShellExecutor getMainExecutor() {
253         return mMainExecutor;
254     }
255 
getAnimExecutor()256     public ShellExecutor getAnimExecutor() {
257         return mAnimExecutor;
258     }
259 
260     /** Only use this in tests. This is used to avoid running animations during tests. */
261     @VisibleForTesting
replaceDefaultHandlerForTest(TransitionHandler handler)262     void replaceDefaultHandlerForTest(TransitionHandler handler) {
263         mHandlers.set(0, handler);
264     }
265 
266     /** Register a remote transition to be used when `filter` matches an incoming transition */
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)267     public void registerRemote(@NonNull TransitionFilter filter,
268             @NonNull RemoteTransition remoteTransition) {
269         mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
270     }
271 
272     /** Unregisters a remote transition and all associated filters */
unregisterRemote(@onNull RemoteTransition remoteTransition)273     public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
274         mRemoteTransitionHandler.removeFiltered(remoteTransition);
275     }
276 
277     /** Registers an observer on the lifecycle of transitions. */
registerObserver(@onNull TransitionObserver observer)278     public void registerObserver(@NonNull TransitionObserver observer) {
279         mObservers.add(observer);
280     }
281 
282     /** Unregisters the observer. */
unregisterObserver(@onNull TransitionObserver observer)283     public void unregisterObserver(@NonNull TransitionObserver observer) {
284         mObservers.remove(observer);
285     }
286 
287     /** Boosts the process priority of remote animation player. */
setRunningRemoteTransitionDelegate(IApplicationThread appThread)288     public static void setRunningRemoteTransitionDelegate(IApplicationThread appThread) {
289         if (appThread == null) return;
290         try {
291             ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(appThread);
292         } catch (SecurityException e) {
293             Log.e(TAG, "Unable to boost animation process. This should only happen"
294                     + " during unit tests");
295         } catch (RemoteException e) {
296             e.rethrowFromSystemServer();
297         }
298     }
299 
300     /**
301      * Runs the given {@code runnable} when the last active transition has finished, or immediately
302      * if there are currently no active transitions.
303      *
304      * <p>This method should be called on the Shell main-thread, where the given {@code runnable}
305      * will be executed when the last active transition is finished.
306      */
runOnIdle(Runnable runnable)307     public void runOnIdle(Runnable runnable) {
308         if (mActiveTransitions.isEmpty()) {
309             runnable.run();
310         } else {
311             mRunWhenIdleQueue.add(runnable);
312         }
313     }
314 
315     /** @return true if the transition was triggered by opening something vs closing something */
isOpeningType(@indowManager.TransitionType int type)316     public static boolean isOpeningType(@WindowManager.TransitionType int type) {
317         return type == TRANSIT_OPEN
318                 || type == TRANSIT_TO_FRONT
319                 || type == TRANSIT_KEYGUARD_GOING_AWAY;
320     }
321 
322     /** @return true if the transition was triggered by closing something vs opening something */
isClosingType(@indowManager.TransitionType int type)323     public static boolean isClosingType(@WindowManager.TransitionType int type) {
324         return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
325     }
326 
327     /**
328      * Sets up visibility/alpha/transforms to resemble the starting state of an animation.
329      */
setupStartState(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)330     private static void setupStartState(@NonNull TransitionInfo info,
331             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
332         boolean isOpening = isOpeningType(info.getType());
333         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
334             final TransitionInfo.Change change = info.getChanges().get(i);
335             if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
336                 // Currently system windows are controlled by WindowState, so don't change their
337                 // surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
338                 // This includes Wallpaper (always z-ordered at bottom) and IME (associated with
339                 // app), because there may not be a transition associated with their visibility
340                 // changes, and currently they don't need transition animation.
341                 continue;
342             }
343             final SurfaceControl leash = change.getLeash();
344             final int mode = info.getChanges().get(i).getMode();
345 
346             if (mode == TRANSIT_TO_FRONT
347                     && ((change.getStartAbsBounds().height() != change.getEndAbsBounds().height()
348                     || change.getStartAbsBounds().width() != change.getEndAbsBounds().width()))) {
349                 // When the window is moved to front with a different size, make sure the crop is
350                 // updated to prevent it from using the old crop.
351                 t.setWindowCrop(leash, change.getEndAbsBounds().width(),
352                         change.getEndAbsBounds().height());
353             }
354 
355             // Don't move anything that isn't independent within its parents
356             if (!TransitionInfo.isIndependent(change, info)) {
357                 if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
358                     t.show(leash);
359                     t.setMatrix(leash, 1, 0, 0, 1);
360                     t.setAlpha(leash, 1.f);
361                     t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
362                 }
363                 continue;
364             }
365 
366             if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
367                 t.show(leash);
368                 t.setMatrix(leash, 1, 0, 0, 1);
369                 if (isOpening
370                         // If this is a transferred starting window, we want it immediately visible.
371                         && (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
372                     t.setAlpha(leash, 0.f);
373                 }
374             } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
375                 finishT.hide(leash);
376             }
377         }
378     }
379 
380     /**
381      * Reparents all participants into a shared parent and orders them based on: the global transit
382      * type, their transit mode, and their destination z-order.
383      */
setupAnimHierarchy(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)384     private static void setupAnimHierarchy(@NonNull TransitionInfo info,
385             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
386         boolean isOpening = isOpeningType(info.getType());
387         if (info.getRootLeash().isValid()) {
388             t.show(info.getRootLeash());
389         }
390         final int numChanges = info.getChanges().size();
391         // Put animating stuff above this line and put static stuff below it.
392         final int zSplitLine = numChanges + 1;
393         // changes should be ordered top-to-bottom in z
394         for (int i = numChanges - 1; i >= 0; --i) {
395             final TransitionInfo.Change change = info.getChanges().get(i);
396             final SurfaceControl leash = change.getLeash();
397             final int mode = change.getMode();
398 
399             // Don't reparent anything that isn't independent within its parents
400             if (!TransitionInfo.isIndependent(change, info)) {
401                 continue;
402             }
403 
404             boolean hasParent = change.getParent() != null;
405 
406             if (!hasParent) {
407                 t.reparent(leash, info.getRootLeash());
408                 t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
409                         change.getStartAbsBounds().top - info.getRootOffset().y);
410             }
411             final int layer;
412             // Put all the OPEN/SHOW on top
413             if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
414                 // Wallpaper is always at the bottom.
415                 layer = -zSplitLine;
416             } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
417                 if (isOpening) {
418                     // put on top
419                     layer = zSplitLine + numChanges - i;
420                 } else {
421                     // put on bottom
422                     layer = zSplitLine - i;
423                 }
424             } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
425                 if (isOpening) {
426                     // put on bottom and leave visible
427                     layer = zSplitLine - i;
428                 } else {
429                     // put on top
430                     layer = zSplitLine + numChanges - i;
431                 }
432             } else { // CHANGE or other
433                 layer = zSplitLine + numChanges - i;
434             }
435             t.setLayer(leash, layer);
436         }
437     }
438 
findActiveTransition(IBinder token)439     private int findActiveTransition(IBinder token) {
440         for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
441             if (mActiveTransitions.get(i).mToken == token) return i;
442         }
443         return -1;
444     }
445 
446     @VisibleForTesting
onTransitionReady(@onNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT)447     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
448             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
449         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
450                 transitionToken, info);
451         final int activeIdx = findActiveTransition(transitionToken);
452         if (activeIdx < 0) {
453             throw new IllegalStateException("Got transitionReady for non-active transition "
454                     + transitionToken + ". expecting one of "
455                     + Arrays.toString(mActiveTransitions.stream().map(
456                             activeTransition -> activeTransition.mToken).toArray()));
457         }
458 
459         for (int i = 0; i < mObservers.size(); ++i) {
460             mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
461         }
462 
463         if (!info.getRootLeash().isValid()) {
464             // Invalid root-leash implies that the transition is empty/no-op, so just do
465             // housekeeping and return.
466             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
467                     transitionToken, info);
468             t.apply();
469             finishT.apply();
470             onAbort(transitionToken);
471             return;
472         }
473 
474         final int changeSize = info.getChanges().size();
475         boolean taskChange = false;
476         boolean transferStartingWindow = false;
477         boolean allOccluded = changeSize > 0;
478         for (int i = changeSize - 1; i >= 0; --i) {
479             final TransitionInfo.Change change = info.getChanges().get(i);
480             taskChange |= change.getTaskInfo() != null;
481             transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
482             if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
483                 allOccluded = false;
484             }
485         }
486         // There does not need animation when:
487         // A. Transfer starting window. Apply transfer starting window directly if there is no other
488         // task change. Since this is an activity->activity situation, we can detect it by selecting
489         // transitions with only 2 changes where neither are tasks and one is a starting-window
490         // recipient.
491         if (!taskChange && transferStartingWindow && changeSize == 2
492                 // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
493                 // changes are underneath another change.
494                 || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
495                 && allOccluded)) {
496             t.apply();
497             finishT.apply();
498             // Treat this as an abort since we are bypassing any merge logic and effectively
499             // finishing immediately.
500             onAbort(transitionToken);
501             releaseSurfaces(info);
502             return;
503         }
504 
505         final ActiveTransition active = mActiveTransitions.get(activeIdx);
506         active.mInfo = info;
507         active.mStartT = t;
508         active.mFinishT = finishT;
509         setupStartState(active.mInfo, active.mStartT, active.mFinishT);
510 
511         if (activeIdx > 0) {
512             // This is now playing at the same time as an existing animation, so try merging it.
513             attemptMergeTransition(mActiveTransitions.get(0), active);
514             return;
515         }
516         // The normal case, just play it.
517         playTransition(active);
518     }
519 
520     /**
521      * Attempt to merge by delegating the transition start to the handler of the currently
522      * playing transition.
523      */
attemptMergeTransition(@onNull ActiveTransition playing, @NonNull ActiveTransition merging)524     void attemptMergeTransition(@NonNull ActiveTransition playing,
525             @NonNull ActiveTransition merging) {
526         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
527                 + " another transition %s is still animating. Notify the animating transition"
528                 + " in case they can be merged", merging.mToken, playing.mToken);
529         playing.mHandler.mergeAnimation(merging.mToken, merging.mInfo, merging.mStartT,
530                 playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
531     }
532 
playTransition(@onNull ActiveTransition active)533     private void playTransition(@NonNull ActiveTransition active) {
534         for (int i = 0; i < mObservers.size(); ++i) {
535             mObservers.get(i).onTransitionStarting(active.mToken);
536         }
537 
538         setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
539 
540         // If a handler already chose to run this animation, try delegating to it first.
541         if (active.mHandler != null) {
542             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
543                     active.mHandler);
544             boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo,
545                     active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb));
546             if (consumed) {
547                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
548                 return;
549             }
550         }
551         // Otherwise give every other handler a chance
552         active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
553                 active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler);
554     }
555 
556     /**
557      * Gives every handler (in order) a chance to animate until one consumes the transition.
558      * @return the handler which consumed the transition.
559      */
dispatchTransition(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip)560     TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
561             @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
562             @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) {
563         for (int i = mHandlers.size() - 1; i >= 0; --i) {
564             if (mHandlers.get(i) == skip) continue;
565             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
566                     mHandlers.get(i));
567             boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
568                     finishCB);
569             if (consumed) {
570                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
571                         mHandlers.get(i));
572                 return mHandlers.get(i);
573             }
574         }
575         throw new IllegalStateException(
576                 "This shouldn't happen, maybe the default handler is broken.");
577     }
578 
579     /**
580      * Gives every handler (in order) a chance to handle request until one consumes the transition.
581      * @return the WindowContainerTransaction given by the handler which consumed the transition.
582      */
dispatchRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip)583     public WindowContainerTransaction dispatchRequest(@NonNull IBinder transition,
584             @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip) {
585         for (int i = mHandlers.size() - 1; i >= 0; --i) {
586             if (mHandlers.get(i) == skip) continue;
587             WindowContainerTransaction wct = mHandlers.get(i).handleRequest(transition, request);
588             if (wct != null) {
589                 return wct;
590             }
591         }
592         return null;
593     }
594 
595     /** Special version of finish just for dealing with no-op/invalid transitions. */
onAbort(IBinder transition)596     private void onAbort(IBinder transition) {
597         onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
598     }
599 
onFinish(IBinder transition, @Nullable WindowContainerTransaction wct, @Nullable WindowContainerTransactionCallback wctCB)600     private void onFinish(IBinder transition,
601             @Nullable WindowContainerTransaction wct,
602             @Nullable WindowContainerTransactionCallback wctCB) {
603         onFinish(transition, wct, wctCB, false /* abort */);
604     }
605 
606     /**
607      * Releases an info's animation-surfaces. These don't need to persist and we need to release
608      * them asap so that SF can free memory sooner.
609      */
releaseSurfaces(@ullable TransitionInfo info)610     private void releaseSurfaces(@Nullable TransitionInfo info) {
611         if (info == null) return;
612         info.releaseAnimSurfaces();
613     }
614 
onFinish(IBinder transition, @Nullable WindowContainerTransaction wct, @Nullable WindowContainerTransactionCallback wctCB, boolean abort)615     private void onFinish(IBinder transition,
616             @Nullable WindowContainerTransaction wct,
617             @Nullable WindowContainerTransactionCallback wctCB,
618             boolean abort) {
619         int activeIdx = findActiveTransition(transition);
620         if (activeIdx < 0) {
621             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
622                     + " a handler didn't properly deal with a merge.", new RuntimeException());
623             return;
624         } else if (activeIdx > 0) {
625             // This transition was merged.
626             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
627                     + " %s", abort, transition);
628             final ActiveTransition active = mActiveTransitions.get(activeIdx);
629             active.mMerged = true;
630             active.mAborted = abort;
631             if (active.mHandler != null) {
632                 active.mHandler.onTransitionConsumed(
633                         active.mToken, abort, abort ? null : active.mFinishT);
634             }
635             for (int i = 0; i < mObservers.size(); ++i) {
636                 mObservers.get(i).onTransitionMerged(
637                         active.mToken, mActiveTransitions.get(0).mToken);
638             }
639             return;
640         }
641         final ActiveTransition active = mActiveTransitions.get(activeIdx);
642         active.mAborted = abort;
643         if (active.mAborted && active.mHandler != null) {
644             // Notifies to clean-up the aborted transition.
645             active.mHandler.onTransitionConsumed(
646                     transition, true /* aborted */, null /* finishTransaction */);
647         }
648         for (int i = 0; i < mObservers.size(); ++i) {
649             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
650         }
651         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
652                 "Transition animation finished (abort=%b), notifying core %s", abort, transition);
653         if (active.mStartT != null) {
654             // Applied by now, so close immediately. Do not set to null yet, though, since nullness
655             // is used later to disambiguate malformed transitions.
656             active.mStartT.close();
657         }
658         // Merge all relevant transactions together
659         SurfaceControl.Transaction fullFinish = active.mFinishT;
660         for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
661             final ActiveTransition toMerge = mActiveTransitions.get(iA);
662             if (!toMerge.mMerged) break;
663             // aborted transitions have no start/finish transactions
664             if (mActiveTransitions.get(iA).mStartT == null) break;
665             if (fullFinish == null) {
666                 fullFinish = new SurfaceControl.Transaction();
667             }
668             // Include start. It will be a no-op if it was already applied. Otherwise, we need it
669             // to maintain consistent state.
670             fullFinish.merge(mActiveTransitions.get(iA).mStartT);
671             fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
672         }
673         if (fullFinish != null) {
674             fullFinish.apply();
675         }
676         // Now perform all the finishes.
677         releaseSurfaces(active.mInfo);
678         mActiveTransitions.remove(activeIdx);
679         mOrganizer.finishTransition(transition, wct, wctCB);
680         while (activeIdx < mActiveTransitions.size()) {
681             if (!mActiveTransitions.get(activeIdx).mMerged) break;
682             ActiveTransition merged = mActiveTransitions.remove(activeIdx);
683             mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
684             releaseSurfaces(merged.mInfo);
685         }
686         // sift through aborted transitions
687         while (mActiveTransitions.size() > activeIdx
688                 && mActiveTransitions.get(activeIdx).mAborted) {
689             ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
690             // Notifies to clean-up the aborted transition.
691             if (aborted.mHandler != null) {
692                 aborted.mHandler.onTransitionConsumed(
693                         transition, true /* aborted */, null /* finishTransaction */);
694             }
695             mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
696             for (int i = 0; i < mObservers.size(); ++i) {
697                 mObservers.get(i).onTransitionFinished(aborted.mToken, true);
698             }
699             releaseSurfaces(aborted.mInfo);
700         }
701         if (mActiveTransitions.size() <= activeIdx) {
702             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
703                     + "finished");
704             // Run all runnables from the run-when-idle queue.
705             for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
706                 mRunWhenIdleQueue.get(i).run();
707             }
708             mRunWhenIdleQueue.clear();
709             return;
710         }
711         // Start animating the next active transition
712         final ActiveTransition next = mActiveTransitions.get(activeIdx);
713         if (next.mInfo == null) {
714             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transition after one"
715                     + " finished, but it isn't ready yet.");
716             return;
717         }
718         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transitions after one"
719                 + " finished, so start the next one.");
720         playTransition(next);
721         // Now try to merge the rest of the transitions (re-acquire activeIdx since next may have
722         // finished immediately)
723         activeIdx = findActiveTransition(next.mToken);
724         if (activeIdx < 0) {
725             // This means 'next' finished immediately and thus re-entered this function. Since
726             // that is the case, just return here since all relevant logic has already run in the
727             // re-entered call.
728             return;
729         }
730 
731         // This logic is also convoluted because 'next' may finish immediately in response to any of
732         // the merge requests (eg. if it decided to "cancel" itself).
733         int mergeIdx = activeIdx + 1;
734         while (mergeIdx < mActiveTransitions.size()) {
735             ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
736             if (mergeCandidate.mAborted) {
737                 // transition was aborted, so we can skip for now (still leave it in the list
738                 // so that it gets cleaned-up in the right order).
739                 ++mergeIdx;
740                 continue;
741             }
742             if (mergeCandidate.mMerged) {
743                 throw new IllegalStateException("Can't merge a transition after not-merging"
744                         + " a preceding one.");
745             }
746             attemptMergeTransition(next, mergeCandidate);
747             mergeIdx = findActiveTransition(mergeCandidate.mToken);
748             if (mergeIdx < 0) {
749                 // This means 'next' finished immediately and thus re-entered this function. Since
750                 // that is the case, just return here since all relevant logic has already run in
751                 // the re-entered call.
752                 return;
753             }
754             ++mergeIdx;
755         }
756     }
757 
requestStartTransition(@onNull IBinder transitionToken, @Nullable TransitionRequestInfo request)758     void requestStartTransition(@NonNull IBinder transitionToken,
759             @Nullable TransitionRequestInfo request) {
760         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
761                 transitionToken, request);
762         if (findActiveTransition(transitionToken) >= 0) {
763             throw new RuntimeException("Transition already started " + transitionToken);
764         }
765         final ActiveTransition active = new ActiveTransition();
766         WindowContainerTransaction wct = null;
767         for (int i = mHandlers.size() - 1; i >= 0; --i) {
768             wct = mHandlers.get(i).handleRequest(transitionToken, request);
769             if (wct != null) {
770                 active.mHandler = mHandlers.get(i);
771                 break;
772             }
773         }
774         if (request.getDisplayChange() != null) {
775             TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
776             if (change.getEndRotation() != change.getStartRotation()) {
777                 // Is a rotation, so dispatch to all displayChange listeners
778                 if (wct == null) {
779                     wct = new WindowContainerTransaction();
780                 }
781                 mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
782                         change.getDisplayId(), change.getStartRotation(), change.getEndRotation(),
783                         null /* newDisplayAreaInfo */);
784             }
785         }
786         mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
787         active.mToken = transitionToken;
788         mActiveTransitions.add(active);
789     }
790 
791     /** Start a new transition directly. */
startTransition(@indowManager.TransitionType int type, @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler)792     public IBinder startTransition(@WindowManager.TransitionType int type,
793             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
794         final ActiveTransition active = new ActiveTransition();
795         active.mHandler = handler;
796         active.mToken = mOrganizer.startNewTransition(type, wct);
797         mActiveTransitions.add(active);
798         return active.mToken;
799     }
800 
801     /**
802      * Interface for a callback that must be called after a TransitionHandler finishes playing an
803      * animation.
804      */
805     public interface TransitionFinishCallback {
806         /**
807          * This must be called on the main thread when a transition finishes playing an animation.
808          * The transition must not touch the surfaces after this has been called.
809          *
810          * @param wct A WindowContainerTransaction to run along with the transition clean-up.
811          * @param wctCB A sync callback that will be run when the transition clean-up is done and
812          *              wct has been applied.
813          */
onTransitionFinished(@ullable WindowContainerTransaction wct, @Nullable WindowContainerTransactionCallback wctCB)814         void onTransitionFinished(@Nullable WindowContainerTransaction wct,
815                 @Nullable WindowContainerTransactionCallback wctCB);
816     }
817 
818     /**
819      * Interface for something which can handle a subset of transitions.
820      */
821     public interface TransitionHandler {
822         /**
823          * Starts a transition animation. This is always called if handleRequest returned non-null
824          * for a particular transition. Otherwise, it is only called if no other handler before
825          * it handled the transition.
826          * @param startTransaction the transaction given to the handler to be applied before the
827          *                         transition animation. Note the handler is expected to call on
828          *                         {@link SurfaceControl.Transaction#apply()} for startTransaction.
829          * @param finishTransaction the transaction given to the handler to be applied after the
830          *                       transition animation. Unlike startTransaction, the handler is NOT
831          *                       expected to apply this transaction. The Transition system will
832          *                       apply it when finishCallback is called.
833          * @param finishCallback Call this when finished. This MUST be called on main thread.
834          * @return true if transition was handled, false if not (falls-back to default).
835          */
startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback)836         boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
837                 @NonNull SurfaceControl.Transaction startTransaction,
838                 @NonNull SurfaceControl.Transaction finishTransaction,
839                 @NonNull TransitionFinishCallback finishCallback);
840 
841         /**
842          * Attempts to merge a different transition's animation into an animation that this handler
843          * is currently playing. If a merge is not possible/supported, this should be a no-op.
844          *
845          * This gets called if another transition becomes ready while this handler is still playing
846          * an animation. This is called regardless of whether this handler claims to support that
847          * particular transition or not.
848          *
849          * When this happens, there are 2 options:
850          *  1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
851          *  2. Merge the incoming transition into this one. The implementation is up to this
852          *     handler. To indicate that this handler has "consumed" the merge transition, it
853          *     must call the finishCallback immediately, or at-least before the original
854          *     transition's finishCallback is called.
855          *
856          * @param transition This is the transition that wants to be merged.
857          * @param info Information about what is changing in the transition.
858          * @param t Contains surface changes that resulted from the transition.
859          * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
860          *                    one this handler is currently already animating).
861          * @param finishCallback Call this if merged. This MUST be called on main thread.
862          */
mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback)863         default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
864                 @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
865                 @NonNull TransitionFinishCallback finishCallback) { }
866 
867         /**
868          * Potentially handles a startTransition request.
869          *
870          * @param transition The transition whose start is being requested.
871          * @param request Information about what is requested.
872          * @return WCT to apply with transition-start or null. If a WCT is returned here, this
873          *         handler will be the first in line to animate.
874          */
875         @Nullable
handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)876         WindowContainerTransaction handleRequest(@NonNull IBinder transition,
877                 @NonNull TransitionRequestInfo request);
878 
879         /**
880          * Called when a transition which was already "claimed" by this handler has been merged
881          * into another animation or has been aborted. Gives this handler a chance to clean-up any
882          * expectations.
883          *
884          * @param transition The transition been consumed.
885          * @param aborted Whether the transition is aborted or not.
886          * @param finishTransaction The transaction to be applied after the transition animated.
887          */
onTransitionConsumed(@onNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishTransaction)888         default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
889                 @Nullable SurfaceControl.Transaction finishTransaction) { }
890 
891         /**
892          * Sets transition animation scale settings value to handler.
893          *
894          * @param scale The setting value of transition animation scale.
895          */
setAnimScaleSetting(float scale)896         default void setAnimScaleSetting(float scale) {}
897     }
898 
899     /**
900      * Interface for something that needs to know the lifecycle of some transitions, but never
901      * handles any transition by itself.
902      */
903     public interface TransitionObserver {
904         /**
905          * Called when the transition is ready to play. It may later be merged into other
906          * transitions. Note this doesn't mean this transition will be played anytime soon.
907          *
908          * @param transition the unique token of this transition
909          * @param startTransaction the transaction given to the handler to be applied before the
910          *                         transition animation. This will be applied when the transition
911          *                         handler that handles this transition starts the transition.
912          * @param finishTransaction the transaction given to the handler to be applied after the
913          *                          transition animation. The Transition system will apply it when
914          *                          finishCallback is called by the transition handler.
915          */
onTransitionReady(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction)916         void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
917                 @NonNull SurfaceControl.Transaction startTransaction,
918                 @NonNull SurfaceControl.Transaction finishTransaction);
919 
920         /**
921          * Called when the transition is starting to play. It isn't called for merged transitions.
922          *
923          * @param transition the unique token of this transition
924          */
onTransitionStarting(@onNull IBinder transition)925         void onTransitionStarting(@NonNull IBinder transition);
926 
927         /**
928          * Called when a transition is merged into another transition. There won't be any following
929          * lifecycle calls for the merged transition.
930          *
931          * @param merged the unique token of the transition that's merged to another one
932          * @param playing the unique token of the transition that accepts the merge
933          */
onTransitionMerged(@onNull IBinder merged, @NonNull IBinder playing)934         void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing);
935 
936         /**
937          * Called when the transition is finished. This isn't called for merged transitions.
938          *
939          * @param transition the unique token of this transition
940          * @param aborted {@code true} if this transition is aborted; {@code false} otherwise.
941          */
onTransitionFinished(@onNull IBinder transition, boolean aborted)942         void onTransitionFinished(@NonNull IBinder transition, boolean aborted);
943     }
944 
945     @BinderThread
946     private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
947         @Override
onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo, SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)948         public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
949                 SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
950                 throws RemoteException {
951             mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
952                     iBinder, transitionInfo, t, finishT));
953         }
954 
955         @Override
requestStartTransition(IBinder iBinder, TransitionRequestInfo request)956         public void requestStartTransition(IBinder iBinder,
957                 TransitionRequestInfo request) throws RemoteException {
958             mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
959         }
960     }
961 
962     /**
963      * The interface for calls from outside the Shell, within the host process.
964      */
965     @ExternalThread
966     private class ShellTransitionImpl implements ShellTransitions {
967         @Override
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)968         public void registerRemote(@NonNull TransitionFilter filter,
969                 @NonNull RemoteTransition remoteTransition) {
970             mMainExecutor.execute(() -> {
971                 mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
972             });
973         }
974 
975         @Override
unregisterRemote(@onNull RemoteTransition remoteTransition)976         public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
977             mMainExecutor.execute(() -> {
978                 mRemoteTransitionHandler.removeFiltered(remoteTransition);
979             });
980         }
981     }
982 
983     /**
984      * The interface for calls from outside the host process.
985      */
986     @BinderThread
987     private static class IShellTransitionsImpl extends IShellTransitions.Stub
988             implements ExternalInterfaceBinder {
989         private Transitions mTransitions;
990 
IShellTransitionsImpl(Transitions transitions)991         IShellTransitionsImpl(Transitions transitions) {
992             mTransitions = transitions;
993         }
994 
995         /**
996          * Invalidates this instance, preventing future calls from updating the controller.
997          */
998         @Override
invalidate()999         public void invalidate() {
1000             mTransitions = null;
1001         }
1002 
1003         @Override
registerRemote(@onNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition)1004         public void registerRemote(@NonNull TransitionFilter filter,
1005                 @NonNull RemoteTransition remoteTransition) {
1006             executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
1007                     (transitions) -> {
1008                         transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
1009                     });
1010         }
1011 
1012         @Override
unregisterRemote(@onNull RemoteTransition remoteTransition)1013         public void unregisterRemote(@NonNull RemoteTransition remoteTransition) {
1014             executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
1015                     (transitions) -> {
1016                         transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
1017                     });
1018         }
1019     }
1020 
1021     private class SettingsObserver extends ContentObserver {
1022 
SettingsObserver()1023         SettingsObserver() {
1024             super(null);
1025         }
1026 
1027         @Override
onChange(boolean selfChange)1028         public void onChange(boolean selfChange) {
1029             super.onChange(selfChange);
1030             mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
1031 
1032             mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
1033         }
1034     }
1035 }
1036