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