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