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