1 /* 2 * Copyright (C) 2020 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.server.wm; 18 19 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; 20 import static android.view.WindowManager.TRANSIT_CHANGE; 21 import static android.view.WindowManager.TRANSIT_CLOSE; 22 import static android.view.WindowManager.TRANSIT_NONE; 23 24 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; 25 26 import android.annotation.ColorInt; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.app.ActivityManager; 30 import android.app.IApplicationThread; 31 import android.app.WindowConfiguration; 32 import android.graphics.Point; 33 import android.graphics.Rect; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.IRemoteCallback; 37 import android.os.RemoteException; 38 import android.os.SystemClock; 39 import android.os.SystemProperties; 40 import android.util.ArrayMap; 41 import android.util.Pair; 42 import android.util.Slog; 43 import android.util.SparseArray; 44 import android.util.TimeUtils; 45 import android.util.proto.ProtoOutputStream; 46 import android.view.Display; 47 import android.view.WindowManager; 48 import android.window.ITransitionMetricsReporter; 49 import android.window.ITransitionPlayer; 50 import android.window.RemoteTransition; 51 import android.window.TransitionInfo; 52 import android.window.TransitionRequestInfo; 53 import android.window.WindowContainerTransaction; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.protolog.ProtoLog; 58 import com.android.internal.protolog.WmProtoLogGroups; 59 import com.android.server.FgThread; 60 import com.android.window.flags.Flags; 61 62 import java.util.ArrayList; 63 import java.util.function.Consumer; 64 import java.util.function.LongConsumer; 65 66 /** 67 * Handles all the aspects of recording (collecting) and synchronizing transitions. This is only 68 * concerned with the WM changes. The actual animations are handled by the Player. 69 * 70 * Currently, only 1 transition can be the primary "collector" at a time. This is because WM changes 71 * are still performed in a "global" manner. However, collecting can actually be broken into 72 * two phases: 73 * 1. Actually making WM changes and recording the participating containers. 74 * 2. Waiting for the participating containers to become ready (eg. redrawing content). 75 * Because (2) takes most of the time AND doesn't change WM, we can actually have multiple 76 * transitions in phase (2) concurrently with one in phase (1). We refer to this arrangement as 77 * "parallel" collection even though there is still only ever 1 transition actually able to gain 78 * participants. 79 * 80 * Parallel collection happens when the "primary collector" has finished "setup" (phase 1) and is 81 * just waiting. At this point, another transition can start collecting. When this happens, the 82 * first transition is moved to a "waiting" list and the new transition becomes the "primary 83 * collector". If at any time, the "primary collector" moves to playing before one of the waiting 84 * transitions, then the first waiting transition will move back to being the "primary collector". 85 * This maintains the "global"-like abstraction that the rest of WM currently expects. 86 * 87 * When a transition move-to-playing, we check it against all other playing transitions. If it 88 * doesn't overlap with them, it can also animate in parallel. In this case it will be assigned a 89 * new "track". "tracks" are a way to communicate to the player about which transitions need to be 90 * played serially with each-other. So, if we find that a transition overlaps with other transitions 91 * in one track, the transition will be assigned to that track. If, however, the transition overlaps 92 * with transition in >1 track, we will actually just mark it as SYNC meaning it can't actually 93 * play until all prior transition animations finish. This is heavy-handed because it is a fallback 94 * situation and supporting something fancier would be unnecessarily complicated. 95 */ 96 class TransitionController { 97 private static final String TAG = "TransitionController"; 98 99 /** Whether to use shell-transitions rotation instead of fixed-rotation. */ 100 private static final boolean SHELL_TRANSITIONS_ROTATION = 101 SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); 102 103 /** Which sync method to use for transition syncs. */ 104 static final int SYNC_METHOD = 105 android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", false) 106 ? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE; 107 108 /** The same as legacy APP_TRANSITION_TIMEOUT_MS. */ 109 private static final int DEFAULT_TIMEOUT_MS = 5000; 110 /** Less duration for CHANGE type because it does not involve app startup. */ 111 private static final int CHANGE_TIMEOUT_MS = 2000; 112 113 // State constants to line-up with legacy app-transition proto expectations. 114 private static final int LEGACY_STATE_IDLE = 0; 115 private static final int LEGACY_STATE_READY = 1; 116 private static final int LEGACY_STATE_RUNNING = 2; 117 118 private final ArrayList<TransitionPlayerRecord> mTransitionPlayers = new ArrayList<>(); 119 final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter(); 120 121 final ActivityTaskManagerService mAtm; 122 BLASTSyncEngine mSyncEngine; 123 124 final RemotePlayer mRemotePlayer; 125 SnapshotController mSnapshotController; 126 TransitionTracer mTransitionTracer; 127 128 private boolean mFullReadyTracking = false; 129 130 private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners = 131 new ArrayList<>(); 132 133 /** 134 * List of runnables to run when there are no ongoing transitions. Use this for state-validation 135 * checks (eg. to recover from incomplete states). Eventually this should be removed. 136 */ 137 final ArrayList<Runnable> mStateValidators = new ArrayList<>(); 138 139 /** 140 * List of activity-records whose visibility changed outside the main/tracked part of a 141 * transition (eg. in the finish-transaction). These will be checked when idle to recover from 142 * degenerate states. 143 */ 144 final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>(); 145 146 /** 147 * List of activity-level participants. ActivityRecord is not expected to change independently, 148 * however, recent compatibility logic can now cause this at arbitrary times determined by 149 * client code. If it happens during an animation, the surface can be left at the wrong spot. 150 * TODO(b/290237710) remove when compat logic is moved. 151 */ 152 final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>(); 153 154 /** 155 * List of display areas which were last sent as "closing"-type and haven't yet had a 156 * corresponding "opening"-type transition. A mismatch here is usually related to issues in 157 * keyguard unlock. 158 */ 159 final ArrayList<DisplayArea> mValidateDisplayVis = new ArrayList<>(); 160 161 /** 162 * Currently playing transitions (in the order they were started). When finished, records are 163 * removed from this list. 164 */ 165 private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>(); 166 int mTrackCount = 0; 167 168 /** The currently finishing transition. */ 169 Transition mFinishingTransition; 170 171 /** 172 * The windows that request to be invisible while it is in transition. After the transition 173 * is finished and the windows are no longer animating, their surfaces will be destroyed. 174 */ 175 final ArrayList<WindowState> mAnimatingExitWindows = new ArrayList<>(); 176 177 final Lock mRunningLock = new Lock(); 178 179 static class QueuedTransition { 180 final Transition mTransition; 181 final OnStartCollect mOnStartCollect; 182 final BLASTSyncEngine.SyncGroup mLegacySync; 183 QueuedTransition(Transition transition, OnStartCollect onStartCollect)184 QueuedTransition(Transition transition, OnStartCollect onStartCollect) { 185 mTransition = transition; 186 mOnStartCollect = onStartCollect; 187 mLegacySync = null; 188 } 189 QueuedTransition(BLASTSyncEngine.SyncGroup legacySync, OnStartCollect onStartCollect)190 QueuedTransition(BLASTSyncEngine.SyncGroup legacySync, OnStartCollect onStartCollect) { 191 mTransition = null; 192 mOnStartCollect = onStartCollect; 193 mLegacySync = legacySync; 194 } 195 } 196 197 private final ArrayList<QueuedTransition> mQueuedTransitions = new ArrayList<>(); 198 199 /** 200 * The transition currently being constructed (collecting participants). Unless interrupted, 201 * all WM changes will go into this. 202 */ 203 private Transition mCollectingTransition = null; 204 205 /** 206 * The transitions that are complete but still waiting for participants to become ready 207 */ 208 final ArrayList<Transition> mWaitingTransitions = new ArrayList<>(); 209 210 /** 211 * The (non alwaysOnTop) tasks which were reported as on-top of their display most recently 212 * within a cluster of simultaneous transitions. If tasks are nested, all the tasks that are 213 * parents of the on-top task are also included. This is used to decide which transitions 214 * report which on-top changes. 215 */ 216 final SparseArray<ArrayList<Task>> mLatestOnTopTasksReported = new SparseArray<>(); 217 218 /** 219 * `true` when building surface layer order for the finish transaction. We want to prevent 220 * wm from touching z-order of surfaces during transitions, but we still need to be able to 221 * calculate the layers for the finishTransaction. So, when assigning layers into the finish 222 * transaction, set this to true so that the {@link canAssignLayers} will allow it. 223 */ 224 boolean mBuildingFinishLayers = false; 225 226 /** 227 * Whether the surface of navigation bar token is reparented to an app. 228 */ 229 boolean mNavigationBarAttachedToApp = false; 230 231 private boolean mAnimatingState = false; 232 233 final Handler mLoggerHandler = FgThread.getHandler(); 234 235 /** 236 * {@code true} While this waits for the display to become enabled (during boot). While waiting 237 * for the display, all core-initiated transitions will be "local". 238 * Note: This defaults to false so that it doesn't interfere with unit tests. 239 */ 240 boolean mIsWaitingForDisplayEnabled = false; 241 TransitionController(ActivityTaskManagerService atm)242 TransitionController(ActivityTaskManagerService atm) { 243 mAtm = atm; 244 mRemotePlayer = new RemotePlayer(atm); 245 } 246 setWindowManager(WindowManagerService wms)247 void setWindowManager(WindowManagerService wms) { 248 mSnapshotController = wms.mSnapshotController; 249 mTransitionTracer = wms.mTransitionTracer; 250 mIsWaitingForDisplayEnabled = !wms.mDisplayEnabled; 251 registerLegacyListener(wms.mActivityManagerAppTransitionNotifier); 252 setSyncEngine(wms.mSyncEngine); 253 mFullReadyTracking = Flags.transitReadyTracking(); 254 } 255 256 @VisibleForTesting setSyncEngine(BLASTSyncEngine syncEngine)257 void setSyncEngine(BLASTSyncEngine syncEngine) { 258 mSyncEngine = syncEngine; 259 // Check the queue whenever the sync-engine becomes idle. 260 mSyncEngine.addOnIdleListener(this::tryStartCollectFromQueue); 261 } 262 flushRunningTransitions()263 void flushRunningTransitions() { 264 // Temporarily clear so that nothing gets started/queued while flushing 265 final ArrayList<TransitionPlayerRecord> temp = new ArrayList<>(mTransitionPlayers); 266 mTransitionPlayers.clear(); 267 // Clean-up/finish any playing transitions. Backwards since they can remove themselves. 268 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 269 mPlayingTransitions.get(i).cleanUpOnFailure(); 270 } 271 mPlayingTransitions.clear(); 272 // Clean up waiting transitions first since they technically started first. 273 // Backwards since they can remove themselves. 274 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 275 mWaitingTransitions.get(i).abort(); 276 } 277 mWaitingTransitions.clear(); 278 if (mCollectingTransition != null) { 279 mCollectingTransition.abort(); 280 } 281 mRemotePlayer.clear(); 282 mRunningLock.doNotifyLocked(); 283 // Restore the rest of the player stack 284 mTransitionPlayers.addAll(temp); 285 } 286 287 /** @see #createTransition(int, int) */ 288 @NonNull createTransition(int type)289 Transition createTransition(int type) { 290 return createTransition(type, 0 /* flags */); 291 } 292 293 /** 294 * Creates a transition. It can immediately collect participants. 295 */ 296 @NonNull createTransition(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags)297 Transition createTransition(@WindowManager.TransitionType int type, 298 @WindowManager.TransitionFlags int flags) { 299 if (mTransitionPlayers.isEmpty()) { 300 throw new IllegalStateException("Shell Transitions not enabled"); 301 } 302 if (mCollectingTransition != null) { 303 throw new IllegalStateException("Trying to directly start transition collection while " 304 + " collection is already ongoing. Use {@link #startCollectOrQueue} if" 305 + " possible."); 306 } 307 Transition transit = new Transition(type, flags, this, mSyncEngine); 308 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 309 "Creating Transition: %s", transit); 310 moveToCollecting(transit); 311 return transit; 312 } 313 314 /** Starts Collecting */ moveToCollecting(@onNull Transition transition)315 void moveToCollecting(@NonNull Transition transition) { 316 if (mCollectingTransition != null) { 317 throw new IllegalStateException("Simultaneous transition collection not supported."); 318 } 319 if (mTransitionPlayers.isEmpty()) { 320 // If sysui has been killed (by a test) or crashed, we can temporarily have no player 321 // In this case, abort the transition. 322 transition.abort(); 323 return; 324 } 325 mCollectingTransition = transition; 326 // Distinguish change type because the response time is usually expected to be not too long. 327 final long timeoutMs = 328 transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS; 329 mCollectingTransition.startCollecting(timeoutMs); 330 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 331 "Start collecting in Transition: %s", mCollectingTransition); 332 } 333 registerTransitionPlayer(@ullable ITransitionPlayer player, @Nullable WindowProcessController playerProc)334 void registerTransitionPlayer(@Nullable ITransitionPlayer player, 335 @Nullable WindowProcessController playerProc) { 336 if (!mTransitionPlayers.isEmpty()) { 337 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition " 338 + "player %s over %d other players", player.asBinder(), 339 mTransitionPlayers.size()); 340 // flush currently running transitions so that the new player doesn't get 341 // intermediate state 342 flushRunningTransitions(); 343 } else { 344 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Registering transition " 345 + "player %s ", player.asBinder()); 346 } 347 mTransitionPlayers.add(new TransitionPlayerRecord(player, playerProc)); 348 } 349 350 @VisibleForTesting unregisterTransitionPlayer(@onNull ITransitionPlayer player)351 void unregisterTransitionPlayer(@NonNull ITransitionPlayer player) { 352 int idx = mTransitionPlayers.size() - 1; 353 for (; idx >= 0; --idx) { 354 if (mTransitionPlayers.get(idx).mPlayer.asBinder() == player.asBinder()) break; 355 } 356 if (idx < 0) { 357 ProtoLog.w(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Attempt to unregister " 358 + "transition player %s but it isn't registered", player.asBinder()); 359 return; 360 } 361 final boolean needsFlush = idx == (mTransitionPlayers.size() - 1); 362 final TransitionPlayerRecord record = mTransitionPlayers.remove(idx); 363 if (needsFlush) { 364 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering active " 365 + "transition player %s at index=%d leaving %d in stack", player.asBinder(), 366 idx, mTransitionPlayers.size()); 367 } else { 368 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Unregistering transition " 369 + "player %s at index=%d leaving %d in stack", player.asBinder(), idx, 370 mTransitionPlayers.size()); 371 } 372 record.unlinkToDeath(); 373 if (!needsFlush) { 374 // Not the active player, so no need to flush transitions. 375 return; 376 } 377 flushRunningTransitions(); 378 } 379 getTransitionPlayer()380 @Nullable ITransitionPlayer getTransitionPlayer() { 381 return mTransitionPlayers.isEmpty() ? null : mTransitionPlayers.getLast().mPlayer; 382 } 383 isShellTransitionsEnabled()384 boolean isShellTransitionsEnabled() { 385 return !mTransitionPlayers.isEmpty(); 386 } 387 388 /** @return {@code true} if using shell-transitions rotation instead of fixed-rotation. */ useShellTransitionsRotation()389 boolean useShellTransitionsRotation() { 390 return isShellTransitionsEnabled() && SHELL_TRANSITIONS_ROTATION; 391 } 392 useFullReadyTracking()393 boolean useFullReadyTracking() { 394 return mFullReadyTracking; 395 } 396 setFullReadyTrackingForTest(boolean enabled)397 void setFullReadyTrackingForTest(boolean enabled) { 398 mFullReadyTracking = enabled; 399 } 400 401 /** 402 * @return {@code true} if transition is actively collecting changes. This is {@code false} 403 * once a transition is playing 404 */ isCollecting()405 boolean isCollecting() { 406 return mCollectingTransition != null; 407 } 408 409 /** 410 * @return the collecting transition. {@code null} if there is no collecting transition. 411 */ 412 @Nullable getCollectingTransition()413 Transition getCollectingTransition() { 414 if (mCollectingTransition != null && !mCollectingTransition.isCollecting()) { 415 Slog.wtfStack(TAG, "Collecting Transition (#" + mCollectingTransition.getSyncId() 416 + ") is not collecting. state=" + mCollectingTransition.getState()); 417 } 418 return mCollectingTransition; 419 } 420 421 /** 422 * @return the collecting transition sync Id. This should only be called when there is a 423 * collecting transition. 424 */ getCollectingTransitionId()425 int getCollectingTransitionId() { 426 if (mCollectingTransition == null) { 427 throw new IllegalStateException("There is no collecting transition"); 428 } 429 return mCollectingTransition.getSyncId(); 430 } 431 432 /** 433 * @return {@code true} if transition is actively collecting changes and `wc` is one of them. 434 * This is {@code false} once a transition is playing. 435 */ isCollecting(@onNull WindowContainer wc)436 boolean isCollecting(@NonNull WindowContainer wc) { 437 if (mCollectingTransition == null) return false; 438 if (mCollectingTransition.mParticipants.contains(wc)) return true; 439 for (int i = 0; i < mWaitingTransitions.size(); ++i) { 440 if (mWaitingTransitions.get(i).mParticipants.contains(wc)) return true; 441 } 442 return false; 443 } 444 445 /** 446 * @return {@code true} if transition is actively collecting changes and `wc` is one of them 447 * or a descendant of one of them. {@code false} once playing. 448 */ inCollectingTransition(@onNull WindowContainer wc)449 boolean inCollectingTransition(@NonNull WindowContainer wc) { 450 if (!isCollecting()) return false; 451 if (mCollectingTransition.isInTransition(wc)) return true; 452 for (int i = 0; i < mWaitingTransitions.size(); ++i) { 453 if (mWaitingTransitions.get(i).isInTransition(wc)) return true; 454 } 455 return false; 456 } 457 458 /** 459 * @return {@code true} if transition is actively playing. This is not necessarily {@code true} 460 * during collection. 461 */ isPlaying()462 boolean isPlaying() { 463 return !mPlayingTransitions.isEmpty(); 464 } 465 466 /** 467 * @return {@code true} if one of the playing transitions contains `wc`. 468 */ inPlayingTransition(@onNull WindowContainer wc)469 boolean inPlayingTransition(@NonNull WindowContainer wc) { 470 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 471 if (mPlayingTransitions.get(i).isInTransition(wc)) return true; 472 } 473 return false; 474 } 475 476 /** Returns {@code true} if the `wc` is a target of a playing transition. */ isPlayingTarget(@onNull WindowContainer<?> wc)477 boolean isPlayingTarget(@NonNull WindowContainer<?> wc) { 478 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 479 if (Transition.containsChangeFor(wc, mPlayingTransitions.get(i).mTargets)) { 480 return true; 481 } 482 } 483 return false; 484 } 485 486 /** Returns {@code true} if the finishing transition contains `wc`. */ inFinishingTransition(WindowContainer<?> wc)487 boolean inFinishingTransition(WindowContainer<?> wc) { 488 return mFinishingTransition != null && mFinishingTransition.isInTransition(wc); 489 } 490 491 /** @return {@code true} if a transition is running */ inTransition()492 boolean inTransition() { 493 // TODO(shell-transitions): eventually properly support multiple 494 return isCollecting() || isPlaying() || !mQueuedTransitions.isEmpty(); 495 } 496 497 /** @return {@code true} if a transition is running in a participant subtree of wc */ inTransition(@onNull WindowContainer wc)498 boolean inTransition(@NonNull WindowContainer wc) { 499 return inCollectingTransition(wc) || inPlayingTransition(wc); 500 } 501 502 /** Returns {@code true} if the id matches a collecting or playing transition. */ inTransition(int syncId)503 boolean inTransition(int syncId) { 504 if (mCollectingTransition != null && mCollectingTransition.getSyncId() == syncId) { 505 return true; 506 } 507 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 508 if (mPlayingTransitions.get(i).getSyncId() == syncId) { 509 return true; 510 } 511 } 512 return false; 513 } 514 515 /** Returns {@code true} if the display contains a collecting transition. */ isCollectingTransitionOnDisplay(@onNull DisplayContent dc)516 boolean isCollectingTransitionOnDisplay(@NonNull DisplayContent dc) { 517 return mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc); 518 } 519 520 /** Returns {@code true} if the display contains a running or pending transition. */ isTransitionOnDisplay(@onNull DisplayContent dc)521 boolean isTransitionOnDisplay(@NonNull DisplayContent dc) { 522 if (isCollectingTransitionOnDisplay(dc)) { 523 return true; 524 } 525 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 526 if (mWaitingTransitions.get(i).isOnDisplay(dc)) return true; 527 } 528 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 529 if (mPlayingTransitions.get(i).isOnDisplay(dc)) return true; 530 } 531 return false; 532 } 533 isInAodAppearTransition()534 boolean isInAodAppearTransition() { 535 if (mCollectingTransition != null && mCollectingTransition.isInAodAppearTransition()) { 536 return true; 537 } 538 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 539 if (mWaitingTransitions.get(i).isInAodAppearTransition()) return true; 540 } 541 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 542 if (mPlayingTransitions.get(i).isInAodAppearTransition()) return true; 543 } 544 return false; 545 } 546 547 /** 548 * @return A pair of the transition and restore-behind target for the given {@param container}. 549 * @param container An ancestor of a transient-launch activity 550 */ 551 @Nullable getTransientLaunchTransitionAndTarget( @onNull WindowContainer container)552 Pair<Transition, Task> getTransientLaunchTransitionAndTarget( 553 @NonNull WindowContainer container) { 554 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 555 final Transition transition = mPlayingTransitions.get(i); 556 final Task restoreBehindTask = transition.getTransientLaunchRestoreTarget(container); 557 if (restoreBehindTask != null) { 558 return new Pair<>(transition, restoreBehindTask); 559 } 560 } 561 return null; 562 } 563 564 /** 565 * @return The playing transition that is transiently-hiding the given {@param container}, or 566 * null if there isn't one 567 * @param container A participant of a transient-hide transition 568 */ 569 @Nullable getTransientHideTransitionForContainer( @onNull WindowContainer container)570 Transition getTransientHideTransitionForContainer( 571 @NonNull WindowContainer container) { 572 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 573 final Transition transition = mPlayingTransitions.get(i); 574 if (transition.isInTransientHide(container)) { 575 return transition; 576 } 577 } 578 return null; 579 } 580 581 /** Returns {@code true} if the display contains a transient-launch transition. */ hasTransientLaunch(@onNull DisplayContent dc)582 boolean hasTransientLaunch(@NonNull DisplayContent dc) { 583 if (mCollectingTransition != null && mCollectingTransition.hasTransientLaunch() 584 && mCollectingTransition.isOnDisplay(dc)) { 585 return true; 586 } 587 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 588 final Transition transition = mWaitingTransitions.get(i); 589 if (transition.hasTransientLaunch() && transition.isOnDisplay(dc)) { 590 return true; 591 } 592 } 593 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 594 final Transition transition = mPlayingTransitions.get(i); 595 if (transition.hasTransientLaunch() && transition.isOnDisplay(dc)) { 596 return true; 597 } 598 } 599 return false; 600 } 601 isTransientHide(@onNull Task task)602 boolean isTransientHide(@NonNull Task task) { 603 if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) { 604 return true; 605 } 606 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 607 if (mWaitingTransitions.get(i).isInTransientHide(task)) return true; 608 } 609 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 610 if (mPlayingTransitions.get(i).isInTransientHide(task)) return true; 611 } 612 return false; 613 } 614 isTransientVisible(@onNull Task task)615 boolean isTransientVisible(@NonNull Task task) { 616 if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) { 617 return true; 618 } 619 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 620 if (mWaitingTransitions.get(i).isTransientVisible(task)) return true; 621 } 622 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 623 if (mPlayingTransitions.get(i).isTransientVisible(task)) return true; 624 } 625 return false; 626 } 627 canApplyDim(@ullable Task task)628 boolean canApplyDim(@Nullable Task task) { 629 if (task == null) { 630 // Always allow non-activity window. 631 return true; 632 } 633 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 634 if (!mPlayingTransitions.get(i).canApplyDim(task)) { 635 return false; 636 } 637 } 638 return true; 639 } 640 641 /** 642 * During transient-launch, the "behind" app should retain focus during the transition unless 643 * something takes focus from it explicitly (eg. by calling ATMS.setFocusedTask or by another 644 * transition interrupting this one. 645 * 646 * The rules around this are basically: if there is exactly one active transition and `wc` is 647 * the "behind" of a transient launch, then it can retain focus. 648 */ shouldKeepFocus(@onNull WindowContainer wc)649 boolean shouldKeepFocus(@NonNull WindowContainer wc) { 650 if (mCollectingTransition != null) { 651 if (!mPlayingTransitions.isEmpty()) return false; 652 return mCollectingTransition.isInTransientHide(wc); 653 } else if (mPlayingTransitions.size() == 1) { 654 return mPlayingTransitions.get(0).isInTransientHide(wc); 655 } 656 return false; 657 } 658 659 /** 660 * @return {@code true} if {@param ar} is part of a transient-launch activity in the 661 * collecting transition. 662 */ isTransientCollect(@onNull ActivityRecord ar)663 boolean isTransientCollect(@NonNull ActivityRecord ar) { 664 return mCollectingTransition != null && mCollectingTransition.isTransientLaunch(ar); 665 } 666 667 /** 668 * @return {@code true} if {@param ar} is part of a transient-launch activity in an active 669 * transition. 670 */ isTransientLaunch(@onNull ActivityRecord ar)671 boolean isTransientLaunch(@NonNull ActivityRecord ar) { 672 if (isTransientCollect(ar)) { 673 return true; 674 } 675 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 676 if (mWaitingTransitions.get(i).isTransientLaunch(ar)) return true; 677 } 678 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 679 if (mPlayingTransitions.get(i).isTransientLaunch(ar)) return true; 680 } 681 return false; 682 } 683 684 /** 685 * Whether WM can assign layers to window surfaces at this time. This is usually false while 686 * playing, but can be "opened-up" for certain transition operations like calculating layers 687 * for finishTransaction. 688 */ canAssignLayers(@onNull WindowContainer wc)689 boolean canAssignLayers(@NonNull WindowContainer wc) { 690 // Don't build window state into finish transaction in case another window is added or 691 // removed during transition playing. 692 if (mBuildingFinishLayers) { 693 return wc.asWindowState() == null; 694 } 695 // Always allow WindowState to assign layers since it won't affect transition. 696 return wc.asWindowState() != null || (!isPlaying() 697 // Don't assign task or display area layers while collecting. 698 && !((wc.asTask() != null || wc.asDisplayArea() != null) && isCollecting())); 699 } 700 701 @WindowConfiguration.WindowingMode getWindowingModeAtStart(@onNull WindowContainer wc)702 int getWindowingModeAtStart(@NonNull WindowContainer wc) { 703 if (mCollectingTransition == null) return wc.getWindowingMode(); 704 final Transition.ChangeInfo ci = mCollectingTransition.mChanges.get(wc); 705 if (ci == null) { 706 // not part of transition, so use current state. 707 return wc.getWindowingMode(); 708 } 709 return ci.mWindowingMode; 710 } 711 712 @WindowManager.TransitionType getCollectingTransitionType()713 int getCollectingTransitionType() { 714 return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE; 715 } 716 717 /** 718 * Returns {@code true} if the window container is in the collecting transition, and its 719 * collected rotation is different from the target rotation. 720 */ hasCollectingRotationChange(@onNull WindowContainer<?> wc, int targetRotation)721 boolean hasCollectingRotationChange(@NonNull WindowContainer<?> wc, int targetRotation) { 722 final Transition transition = mCollectingTransition; 723 if (transition == null || !transition.mParticipants.contains(wc)) return false; 724 final Transition.ChangeInfo changeInfo = transition.mChanges.get(wc); 725 return changeInfo != null && changeInfo.mRotation != targetRotation; 726 } 727 728 /** Sets the sync method for the display change. */ setDisplaySyncMethod(@onNull TransitionRequestInfo.DisplayChange displayChange, @NonNull DisplayContent displayContent)729 private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange, 730 @NonNull DisplayContent displayContent) { 731 final Rect startBounds = displayChange.getStartAbsBounds(); 732 final Rect endBounds = displayChange.getEndAbsBounds(); 733 if (startBounds == null || endBounds == null) return; 734 setDisplaySyncMethod(startBounds, endBounds, displayContent); 735 } 736 setDisplaySyncMethod(@onNull Rect startBounds, @NonNull Rect endBounds, @NonNull DisplayContent displayContent)737 void setDisplaySyncMethod(@NonNull Rect startBounds, @NonNull Rect endBounds, 738 @NonNull DisplayContent displayContent) { 739 final int startWidth = startBounds.width(); 740 final int startHeight = startBounds.height(); 741 final int endWidth = endBounds.width(); 742 final int endHeight = endBounds.height(); 743 // This is changing screen resolution. Because the screen decor layers are excluded from 744 // screenshot, their draw transactions need to run with the start transaction. 745 if ((endWidth > startWidth) == (endHeight > startHeight) 746 && (endWidth != startWidth || endHeight != startHeight)) { 747 displayContent.forAllWindows(w -> { 748 if (w.mToken.mRoundedCornerOverlay && w.mHasSurface) { 749 w.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST; 750 } 751 }, true /* traverseTopToBottom */); 752 } 753 } 754 755 /** 756 * If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to 757 * start it. Collection can start immediately. 758 * @param trigger if non-null, this is the first container that will be collected 759 * @param readyGroupRef Used to identify which ready-group this request is for. 760 * @return the created transition if created or null otherwise (already global collecting) 761 */ 762 @Nullable requestTransitionIfNeeded(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger, @NonNull WindowContainer readyGroupRef)763 Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, 764 @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger, 765 @NonNull WindowContainer readyGroupRef) { 766 if (mTransitionPlayers.isEmpty()) { 767 return null; 768 } 769 Transition newTransition = null; 770 if (isCollecting()) { 771 // Make the collecting transition wait until this request is ready. 772 mCollectingTransition.setReady(readyGroupRef, false); 773 if ((flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { 774 // Add keyguard flags to affect keyguard visibility 775 mCollectingTransition.addFlag(flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS); 776 } 777 } else { 778 newTransition = requestStartTransition(createTransition(type, flags), 779 trigger != null ? trigger.asTask() : null, null /* remote */, null /* disp */); 780 } 781 return newTransition; 782 } 783 784 /** 785 * Creates a transition and asks the TransitionPlayer (Shell) to 786 * start it. Collection can start immediately. 787 * @param trigger if non-null, this is the first container that will be collected 788 * @return the created transition if created or null otherwise. 789 */ 790 @NonNull requestStartDisplayTransition(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags, @NonNull DisplayContent trigger, @Nullable RemoteTransition remoteTransition, @Nullable TransitionRequestInfo.DisplayChange displayChange)791 Transition requestStartDisplayTransition(@WindowManager.TransitionType int type, 792 @WindowManager.TransitionFlags int flags, @NonNull DisplayContent trigger, 793 @Nullable RemoteTransition remoteTransition, 794 @Nullable TransitionRequestInfo.DisplayChange displayChange) { 795 final Transition newTransition = createTransition(type, flags); 796 requestStartTransition(newTransition, null /* trigger */, remoteTransition, displayChange); 797 if (displayChange != null) { 798 setDisplaySyncMethod(displayChange, trigger); 799 } 800 return newTransition; 801 } 802 803 /** Asks the transition player (shell) to start a created but not yet started transition. */ 804 @NonNull requestStartTransition(@onNull Transition transition, @Nullable Task startTask, @Nullable RemoteTransition remoteTransition, @Nullable TransitionRequestInfo.DisplayChange displayChange)805 Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask, 806 @Nullable RemoteTransition remoteTransition, 807 @Nullable TransitionRequestInfo.DisplayChange displayChange) { 808 if (mIsWaitingForDisplayEnabled) { 809 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 810 "Disabling player for transition #%d because display isn't enabled yet", 811 transition.getSyncId()); 812 transition.mIsPlayerEnabled = false; 813 transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos(); 814 mAtm.mH.post(() -> mAtm.mWindowOrganizerController.startTransition( 815 transition.getToken(), null)); 816 return transition; 817 } 818 if (mTransitionPlayers.isEmpty() || transition.isAborted()) { 819 // Apparently, some tests will kill(and restart) systemui, so there is a chance that 820 // the player might be transiently null. 821 if (transition.isCollecting()) { 822 transition.abort(); 823 } 824 return transition; 825 } 826 try { 827 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 828 "Requesting StartTransition: %s", transition); 829 ActivityManager.RunningTaskInfo startTaskInfo = null; 830 TransitionRequestInfo.PipChange pipChange = null; 831 if (startTask != null) { 832 startTaskInfo = startTask.getTaskInfo(); 833 } 834 835 // set the pip task in the request if provided 836 if (transition.getPipActivity() != null) { 837 ActivityManager.RunningTaskInfo pipTaskInfo = 838 transition.getPipActivity().getTask().getTaskInfo(); 839 ActivityRecord pipActivity = transition.getPipActivity(); 840 if (pipActivity.getTaskFragment() != null 841 && pipActivity.getTaskFragment() != pipActivity.getTask()) { 842 // If the PiP activity is in a TF different from its task, this could be 843 // AE-to-PiP case, so PipChange will have the TF token cached separately. 844 pipChange = new TransitionRequestInfo.PipChange(pipActivity.getTaskFragment() 845 .mRemoteToken.toWindowContainerToken(), pipTaskInfo); 846 } else { 847 pipChange = new TransitionRequestInfo.PipChange(pipTaskInfo); 848 } 849 transition.setPipActivity(null); 850 } 851 852 final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType, 853 startTaskInfo, pipChange, remoteTransition, displayChange, 854 transition.getFlags(), transition.getSyncId()); 855 856 transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos(); 857 transition.mLogger.mRequest = request; 858 mTransitionPlayers.getLast().mPlayer.requestStartTransition( 859 transition.getToken(), request); 860 if (remoteTransition != null) { 861 transition.setRemoteAnimationApp(remoteTransition.getAppThread()); 862 } 863 } catch (RemoteException e) { 864 Slog.e(TAG, "Error requesting transition", e); 865 transition.start(); 866 } 867 return transition; 868 } 869 870 /** 871 * Requests transition for a window container which will be removed or invisible. 872 * @return the new transition if it was created for this request, `null` otherwise. 873 */ requestCloseTransitionIfNeeded(@onNull WindowContainer<?> wc)874 Transition requestCloseTransitionIfNeeded(@NonNull WindowContainer<?> wc) { 875 if (mTransitionPlayers.isEmpty() || isCollecting()) return null; 876 if (!wc.isVisibleRequested()) return null; 877 return requestStartTransition(createTransition(TRANSIT_CLOSE, 0 /* flags */), wc.asTask(), 878 null /* remoteTransition */, null /* displayChange */); 879 } 880 881 /** @see Transition#collect */ collect(@onNull WindowContainer wc)882 void collect(@NonNull WindowContainer wc) { 883 if (mCollectingTransition == null) return; 884 mCollectingTransition.collect(wc); 885 } 886 887 /** @see Transition#collectExistenceChange */ collectExistenceChange(@onNull WindowContainer wc)888 void collectExistenceChange(@NonNull WindowContainer wc) { 889 if (mCollectingTransition == null) return; 890 mCollectingTransition.collectExistenceChange(wc); 891 } 892 893 /** @see Transition#recordTaskOrder */ recordTaskOrder(@onNull WindowContainer wc)894 void recordTaskOrder(@NonNull WindowContainer wc) { 895 if (mCollectingTransition == null) return; 896 mCollectingTransition.recordTaskOrder(wc); 897 } 898 899 /** @see Transition#hasOrderChanges */ hasOrderChanges()900 boolean hasOrderChanges() { 901 if (mCollectingTransition == null) return false; 902 return mCollectingTransition.hasOrderChanges(); 903 } 904 905 /** 906 * Collects the window containers which need to be synced with the changing display area into 907 * the current collecting transition. 908 */ collectForDisplayAreaChange(@onNull DisplayArea<?> wc)909 void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc) { 910 final Transition transition = mCollectingTransition; 911 if (transition == null || !transition.mParticipants.contains(wc)) return; 912 transition.collectVisibleChange(wc); 913 // Collect all visible tasks. 914 wc.forAllLeafTasks(task -> { 915 if (task.isVisible()) { 916 transition.collect(task); 917 } 918 }, true /* traverseTopToBottom */); 919 // Collect all visible non-app windows which need to be drawn before the animation starts. 920 final DisplayContent dc = wc.asDisplayContent(); 921 if (dc != null) { 922 final boolean noAsyncRotation = dc.getAsyncRotationController() == null; 923 wc.forAllWindows(w -> { 924 if (w.mActivityRecord == null && w.isVisible() && !isCollecting(w.mToken) 925 && (noAsyncRotation || !AsyncRotationController.canBeAsync(w.mToken))) { 926 transition.collect(w.mToken); 927 } 928 }, true /* traverseTopToBottom */); 929 } 930 } 931 932 /** 933 * Records that a particular container is changing visibly (ie. something about it is changing 934 * while it remains visible). This only effects windows that are already in the collecting 935 * transition. 936 */ collectVisibleChange(WindowContainer wc)937 void collectVisibleChange(WindowContainer wc) { 938 if (!isCollecting()) return; 939 mCollectingTransition.collectVisibleChange(wc); 940 } 941 942 /** 943 * Records that a particular container has been reparented. This only effects windows that have 944 * already been collected in the transition. This should be called before reparenting because 945 * the old parent may be removed during reparenting, for example: 946 * {@link Task#shouldRemoveSelfOnLastChildRemoval} 947 */ collectReparentChange(@onNull WindowContainer wc, @NonNull WindowContainer newParent)948 void collectReparentChange(@NonNull WindowContainer wc, @NonNull WindowContainer newParent) { 949 if (!isCollecting()) return; 950 mCollectingTransition.collectReparentChange(wc, newParent); 951 } 952 953 /** @see Transition#mStatusBarTransitionDelay */ setStatusBarTransitionDelay(long delay)954 void setStatusBarTransitionDelay(long delay) { 955 if (mCollectingTransition == null) return; 956 mCollectingTransition.mStatusBarTransitionDelay = delay; 957 } 958 959 /** @see Transition#setOverrideAnimation */ setOverrideAnimation(@onNull TransitionInfo.AnimationOptions options, @NonNull ActivityRecord r, @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback)960 void setOverrideAnimation(@NonNull TransitionInfo.AnimationOptions options, 961 @NonNull ActivityRecord r, @Nullable IRemoteCallback startCallback, 962 @Nullable IRemoteCallback finishCallback) { 963 if (mCollectingTransition == null) return; 964 mCollectingTransition.setOverrideAnimation(options, r, startCallback, finishCallback); 965 } 966 967 /** @see Transition#setOverrideBackgroundColor */ setOverrideBackgroundColor(@olorInt int backgroundColor)968 void setOverrideBackgroundColor(@ColorInt int backgroundColor) { 969 if (mCollectingTransition == null) return; 970 mCollectingTransition.setOverrideBackgroundColor(backgroundColor); 971 } 972 setNoAnimation(WindowContainer wc)973 void setNoAnimation(WindowContainer wc) { 974 if (mCollectingTransition == null) return; 975 mCollectingTransition.setNoAnimation(wc); 976 } 977 978 /** @see Transition#setReady */ setReady(WindowContainer wc, boolean ready)979 void setReady(WindowContainer wc, boolean ready) { 980 if (mCollectingTransition == null) return; 981 mCollectingTransition.setReady(wc, ready); 982 } 983 984 /** @see Transition#setReady */ setReady(WindowContainer wc)985 void setReady(WindowContainer wc) { 986 setReady(wc, true); 987 } 988 989 /** @see Transition#deferTransitionReady */ deferTransitionReady()990 void deferTransitionReady() { 991 if (!isShellTransitionsEnabled()) return; 992 if (mCollectingTransition == null) { 993 throw new IllegalStateException("No collecting transition to defer readiness for."); 994 } 995 mCollectingTransition.deferTransitionReady(); 996 } 997 998 /** @see Transition#continueTransitionReady */ continueTransitionReady()999 void continueTransitionReady() { 1000 if (!isShellTransitionsEnabled()) return; 1001 if (mCollectingTransition == null) { 1002 throw new IllegalStateException("No collecting transition to defer readiness for."); 1003 } 1004 mCollectingTransition.continueTransitionReady(); 1005 } 1006 1007 /** @see Transition#finishTransition */ finishTransition(@onNull ActionChain chain)1008 void finishTransition(@NonNull ActionChain chain) { 1009 if (!chain.isFinishing()) { 1010 throw new IllegalStateException("Can't finish on a non-finishing transition " 1011 + chain.mTransition); 1012 } 1013 final Transition record = chain.mTransition; 1014 // It is usually a no-op but make sure that the metric consumer is removed. 1015 mTransitionMetricsReporter.reportAnimationStart(record.getToken(), 0 /* startTime */); 1016 // It is a no-op if the transition did not change the display. 1017 mAtm.endPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); 1018 if (!mPlayingTransitions.contains(record)) { 1019 Slog.e(TAG, "Trying to finish a non-playing transition " + record); 1020 return; 1021 } 1022 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record); 1023 mPlayingTransitions.remove(record); 1024 if (!inTransition()) { 1025 // reset track-count now since shell-side is idle. 1026 mTrackCount = 0; 1027 } 1028 updateRunningRemoteAnimation(record, false /* isPlaying */); 1029 record.finishTransition(chain); 1030 for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) { 1031 final WindowState w = mAnimatingExitWindows.get(i); 1032 if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) { 1033 w.onExitAnimationDone(); 1034 } 1035 if (!w.mAnimatingExit || !w.mHasSurface) { 1036 mAnimatingExitWindows.remove(i); 1037 } 1038 } 1039 mRunningLock.doNotifyLocked(); 1040 // Run state-validation checks when no transitions are active anymore (Note: sometimes 1041 // finish can start a transition, so check afterwards -- eg. pip). 1042 if (!inTransition()) { 1043 validateStates(); 1044 mAtm.mWindowManager.onAnimationFinished(); 1045 } 1046 } 1047 1048 /** Called by {@link Transition#finishTransition} if it committed invisible to any activities */ onCommittedInvisibles()1049 void onCommittedInvisibles() { 1050 if (mCollectingTransition != null) { 1051 mCollectingTransition.mPriorVisibilityMightBeDirty = true; 1052 } 1053 for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { 1054 mWaitingTransitions.get(i).mPriorVisibilityMightBeDirty = true; 1055 } 1056 } 1057 validateStates()1058 private void validateStates() { 1059 for (int i = 0; i < mStateValidators.size(); ++i) { 1060 mStateValidators.get(i).run(); 1061 if (inTransition()) { 1062 // the validator may have started a new transition, so wait for that before 1063 // checking the rest. 1064 mStateValidators.subList(0, i + 1).clear(); 1065 return; 1066 } 1067 } 1068 mStateValidators.clear(); 1069 for (int i = 0; i < mValidateCommitVis.size(); ++i) { 1070 final ActivityRecord ar = mValidateCommitVis.get(i); 1071 if (!ar.isVisibleRequested() && ar.isVisible()) { 1072 Slog.e(TAG, "Uncommitted visibility change: " + ar); 1073 ar.commitVisibility(ar.isVisibleRequested(), false /* layout */, 1074 false /* fromTransition */); 1075 } 1076 } 1077 mValidateCommitVis.clear(); 1078 for (int i = 0; i < mValidateActivityCompat.size(); ++i) { 1079 ActivityRecord ar = mValidateActivityCompat.get(i); 1080 if (ar.getSurfaceControl() == null) continue; 1081 final Point tmpPos = new Point(); 1082 ar.getRelativePosition(tmpPos); 1083 ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y); 1084 } 1085 mValidateActivityCompat.clear(); 1086 for (int i = 0; i < mValidateDisplayVis.size(); ++i) { 1087 final DisplayArea da = mValidateDisplayVis.get(i); 1088 if (!da.isAttached() || da.getSurfaceControl() == null) continue; 1089 if (da.isVisibleRequested()) { 1090 Slog.e(TAG, "DisplayArea became visible outside of a transition: " + da); 1091 da.getSyncTransaction().show(da.getSurfaceControl()); 1092 } 1093 } 1094 mValidateDisplayVis.clear(); 1095 } 1096 onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller)1097 void onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller) { 1098 final boolean isPlaying = !mPlayingTransitions.isEmpty(); 1099 Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying 1100 + " caller=" + caller); 1101 if (!isPlaying) { 1102 WindowContainer.enforceSurfaceVisible(wc); 1103 return; 1104 } 1105 // Update surface visibility after the playing transitions are finished, so the last 1106 // visibility won't be replaced by the finish transaction of transition. 1107 mStateValidators.add(() -> { 1108 if (wc.isVisibleRequested()) { 1109 WindowContainer.enforceSurfaceVisible(wc); 1110 } 1111 }); 1112 } 1113 1114 /** 1115 * Called when the transition has a complete set of participants for its operation. In other 1116 * words, it is when the transition is "ready" but is still waiting for participants to draw. 1117 */ onTransitionPopulated(Transition transition)1118 void onTransitionPopulated(Transition transition) { 1119 tryStartCollectFromQueue(); 1120 } 1121 canStartCollectingNow(@ullable Transition queued)1122 private boolean canStartCollectingNow(@Nullable Transition queued) { 1123 if (mCollectingTransition == null) return true; 1124 // Population (collect until ready) is still serialized, so always wait for that. 1125 if (!mCollectingTransition.isPopulated()) return false; 1126 // Check if queued *can* be independent with all collecting/waiting transitions. 1127 if (!getCanBeIndependent(mCollectingTransition, queued)) return false; 1128 for (int i = 0; i < mWaitingTransitions.size(); ++i) { 1129 if (!getCanBeIndependent(mWaitingTransitions.get(i), queued)) return false; 1130 } 1131 return true; 1132 } 1133 tryStartCollectFromQueue()1134 void tryStartCollectFromQueue() { 1135 if (mQueuedTransitions.isEmpty()) return; 1136 // Only need to try the next one since, even when transition can collect in parallel, 1137 // they still need to serialize on readiness. 1138 final QueuedTransition queued = mQueuedTransitions.get(0); 1139 if (mCollectingTransition != null) { 1140 // If it's a legacy sync, then it needs to wait until there is no collecting transition. 1141 if (queued.mTransition == null) return; 1142 if (!canStartCollectingNow(queued.mTransition)) return; 1143 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1144 "Moving #%d from collecting to waiting.", mCollectingTransition.getSyncId()); 1145 mWaitingTransitions.add(mCollectingTransition); 1146 mCollectingTransition = null; 1147 } else if (mSyncEngine.hasActiveSync()) { 1148 // A legacy transition is on-going, so we must wait. 1149 return; 1150 } 1151 mQueuedTransitions.remove(0); 1152 // This needs to happen immediately to prevent another sync from claiming the syncset 1153 // out-of-order (moveToCollecting calls startSyncSet) 1154 if (queued.mTransition != null) { 1155 moveToCollecting(queued.mTransition); 1156 } else { 1157 // legacy sync 1158 mSyncEngine.startSyncSet(queued.mLegacySync); 1159 } 1160 if (queued.mTransition != null 1161 && queued.mTransition.mType == WindowManager.TRANSIT_SLEEP) { 1162 // SLEEP transitions are special in that they don't collect anything (in fact if they 1163 // do collect things it can cause problems). So, we need to run it's onCollectStarted 1164 // immediately. 1165 queued.mOnStartCollect.onCollectStarted(true /* deferred */); 1166 } else { 1167 // Post this so that the now-playing transition logic isn't interrupted. 1168 mAtm.mH.post(() -> { 1169 synchronized (mAtm.mGlobalLock) { 1170 queued.mOnStartCollect.onCollectStarted(true /* deferred */); 1171 } 1172 }); 1173 } 1174 } 1175 moveToPlaying(Transition transition)1176 void moveToPlaying(Transition transition) { 1177 if (transition == mCollectingTransition) { 1178 mCollectingTransition = null; 1179 if (!mWaitingTransitions.isEmpty()) { 1180 mCollectingTransition = mWaitingTransitions.remove(0); 1181 } 1182 if (mCollectingTransition == null) { 1183 // nothing collecting anymore, so clear order records. 1184 mLatestOnTopTasksReported.clear(); 1185 } 1186 } else { 1187 if (!mWaitingTransitions.remove(transition)) { 1188 throw new IllegalStateException("Trying to move non-collecting transition to" 1189 + "playing " + transition.getSyncId()); 1190 } 1191 } 1192 mPlayingTransitions.add(transition); 1193 updateRunningRemoteAnimation(transition, true /* isPlaying */); 1194 // Sync engine should become idle after this, so the idle listener will check the queue. 1195 } 1196 1197 /** 1198 * Checks if the `queued` transition has the potential to run independently of the 1199 * `collecting` transition. It may still ultimately block in sync-engine or become dependent 1200 * in {@link #getIsIndependent} later. 1201 */ getCanBeIndependent(Transition collecting, @Nullable Transition queued)1202 boolean getCanBeIndependent(Transition collecting, @Nullable Transition queued) { 1203 // For tests 1204 if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL 1205 && collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { 1206 return true; 1207 } 1208 // For recents 1209 if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1210 if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1211 // Must serialize with itself. 1212 return false; 1213 } 1214 // allow this if `collecting` only has activities 1215 for (int i = 0; i < collecting.mParticipants.size(); ++i) { 1216 final WindowContainer wc = collecting.mParticipants.valueAt(i); 1217 final ActivityRecord ar = wc.asActivityRecord(); 1218 if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { 1219 // Is task or above, so can't be independent 1220 return false; 1221 } 1222 if (ar != null && ar.isActivityTypeHomeOrRecents()) { 1223 // It's a recents or home type, so it conflicts. 1224 return false; 1225 } 1226 } 1227 return true; 1228 } else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1229 // We can collect simultaneously with recents if it is populated. This is because 1230 // we know that recents will not collect/trampoline any more stuff. If anything in the 1231 // queued transition overlaps, it will end up just waiting in sync-queue anyways. 1232 return true; 1233 } 1234 return false; 1235 } 1236 1237 /** 1238 * Checks if `incoming` transition can run independently of `running` transition assuming that 1239 * `running` is playing based on its current state. 1240 */ getIsIndependent(Transition running, Transition incoming)1241 static boolean getIsIndependent(Transition running, Transition incoming) { 1242 // For tests 1243 if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL 1244 && incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { 1245 return true; 1246 } 1247 // For now there's only one mutually-independent pair: an all activity-level transition and 1248 // a transient-launch where none of the activities are part of the transient-launch task, 1249 // so the following logic is hard-coded specifically for this. 1250 // Also, we currently restrict valid transient-launches to just recents. 1251 final Transition recents; 1252 final Transition other; 1253 if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS 1254 && running.hasTransientLaunch()) { 1255 if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1256 // Recents can't be independent from itself. 1257 return false; 1258 } 1259 recents = running; 1260 other = incoming; 1261 } else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS 1262 && incoming.hasTransientLaunch()) { 1263 recents = incoming; 1264 other = running; 1265 } else { 1266 return false; 1267 } 1268 // Check against *targets* because that is the post-promotion set of containers that are 1269 // actually animating. 1270 for (int i = 0; i < other.mTargets.size(); ++i) { 1271 final WindowContainer wc = other.mTargets.get(i).mContainer; 1272 final ActivityRecord ar = wc.asActivityRecord(); 1273 if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { 1274 // Is task or above, so for now don't let them be independent. 1275 return false; 1276 } 1277 if (ar != null && recents.isTransientLaunch(ar)) { 1278 // Change overlaps with recents, so serialize. 1279 return false; 1280 } 1281 } 1282 return true; 1283 } 1284 assignTrack(Transition transition, TransitionInfo info)1285 void assignTrack(Transition transition, TransitionInfo info) { 1286 int track = -1; 1287 boolean sync = false; 1288 for (int i = 0; i < mPlayingTransitions.size(); ++i) { 1289 // ignore ourself obviously 1290 if (mPlayingTransitions.get(i) == transition) continue; 1291 if (getIsIndependent(mPlayingTransitions.get(i), transition)) continue; 1292 if (track < 0) { 1293 track = mPlayingTransitions.get(i).mAnimationTrack; 1294 } else if (track != mPlayingTransitions.get(i).mAnimationTrack) { 1295 // At this point, transition overlaps with multiple tracks, so just wait for 1296 // everything 1297 sync = true; 1298 break; 1299 } 1300 } 1301 if (sync) { 1302 track = 0; 1303 } 1304 if (track < 0) { 1305 // Didn't overlap with anything, so give it its own track 1306 track = mTrackCount; 1307 if (track > 0) { 1308 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 1309 "Playing #%d in parallel on track #%d", transition.getSyncId(), track); 1310 } 1311 } 1312 transition.mAnimationTrack = track; 1313 info.setTrack(track); 1314 mTrackCount = Math.max(mTrackCount, track + 1); 1315 if (sync && mTrackCount > 1) { 1316 // If there are >1 tracks, mark as sync so that all tracks finish. 1317 info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC); 1318 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, 1319 "Marking #%d animation as SYNC.", transition.getSyncId()); 1320 } 1321 } 1322 1323 /** Returns {@code true} if a transition is playing or the collecting transition is started. */ isAnimating()1324 boolean isAnimating() { 1325 return mAnimatingState; 1326 } 1327 updateAnimatingState()1328 void updateAnimatingState() { 1329 final boolean animatingState = !mPlayingTransitions.isEmpty() 1330 || (mCollectingTransition != null && mCollectingTransition.isStarted()); 1331 if (animatingState && !mAnimatingState) { 1332 // Note that Transition#start() can be called before adding participants, so the 1333 // enableHighPerfTransition(true) is also called in Transition#recordDisplay. 1334 for (int i = mAtm.mRootWindowContainer.getChildCount() - 1; i >= 0; i--) { 1335 final DisplayContent dc = mAtm.mRootWindowContainer.getChildAt(i); 1336 if (mCollectingTransition != null && mCollectingTransition.shouldUsePerfHint(dc)) { 1337 dc.enableHighPerfTransition(true); 1338 continue; 1339 } 1340 for (int j = mPlayingTransitions.size() - 1; j >= 0; j--) { 1341 if (mPlayingTransitions.get(j).shouldUsePerfHint(dc)) { 1342 dc.enableHighPerfTransition(true); 1343 break; 1344 } 1345 } 1346 } 1347 // Usually transitions put quite a load onto the system already (with all the things 1348 // happening in app), so pause task snapshot persisting to not increase the load. 1349 mSnapshotController.setPause(true); 1350 mAnimatingState = true; 1351 Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */); 1352 } else if (!animatingState && mAnimatingState) { 1353 for (int i = mAtm.mRootWindowContainer.getChildCount() - 1; i >= 0; i--) { 1354 mAtm.mRootWindowContainer.getChildAt(i).enableHighPerfTransition(false); 1355 } 1356 mAtm.mWindowManager.scheduleAnimationLocked(); 1357 mSnapshotController.setPause(false); 1358 mAnimatingState = false; 1359 Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */); 1360 } 1361 } 1362 1363 /** Updates the process state of animation player. */ updateRunningRemoteAnimation(Transition transition, boolean isPlaying)1364 private void updateRunningRemoteAnimation(Transition transition, boolean isPlaying) { 1365 if (mTransitionPlayers.isEmpty()) return; 1366 final TransitionPlayerRecord record = mTransitionPlayers.getLast(); 1367 if (record.mPlayerProc == null) return; 1368 if (isPlaying) { 1369 record.mPlayerProc.setRunningRemoteAnimation(true); 1370 } else if (mPlayingTransitions.isEmpty()) { 1371 record.mPlayerProc.setRunningRemoteAnimation(false); 1372 mRemotePlayer.clear(); 1373 } 1374 } 1375 1376 /** Called when a transition is aborted. This should only be called by {@link Transition} */ onAbort(Transition transition)1377 void onAbort(Transition transition) { 1378 if (transition != mCollectingTransition) { 1379 int waitingIdx = mWaitingTransitions.indexOf(transition); 1380 if (waitingIdx < 0) { 1381 throw new IllegalStateException("Too late for abort."); 1382 } 1383 mWaitingTransitions.remove(waitingIdx); 1384 } else { 1385 mCollectingTransition = null; 1386 if (!mWaitingTransitions.isEmpty()) { 1387 mCollectingTransition = mWaitingTransitions.remove(0); 1388 } 1389 if (mCollectingTransition == null) { 1390 // nothing collecting anymore, so clear order records. 1391 mLatestOnTopTasksReported.clear(); 1392 } 1393 } 1394 // This is called during Transition.abort whose codepath will eventually check the queue 1395 // via sync-engine idle. 1396 } 1397 1398 /** 1399 * Record that the launch of {@param activity} is transient (meaning its lifecycle is currently 1400 * tied to the transition). 1401 * @param restoreBelowTask If non-null, the activity's task will be ordered right below this 1402 * task if requested. 1403 */ setTransientLaunch(@onNull ActivityRecord activity, @Nullable Task restoreBelowTask)1404 void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelowTask) { 1405 if (mCollectingTransition == null) return; 1406 mCollectingTransition.setTransientLaunch(activity, restoreBelowTask); 1407 } 1408 legacyDetachNavigationBarFromApp(@onNull IBinder token)1409 void legacyDetachNavigationBarFromApp(@NonNull IBinder token) { 1410 final Transition transition = Transition.fromBinder(token); 1411 if (transition == null || !mPlayingTransitions.contains(transition)) { 1412 Slog.e(TAG, "Transition isn't playing: " + token); 1413 return; 1414 } 1415 transition.legacyRestoreNavigationBarFromApp(); 1416 } 1417 registerLegacyListener(WindowManagerInternal.AppTransitionListener listener)1418 void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) { 1419 mLegacyListeners.add(listener); 1420 } 1421 unregisterLegacyListener(WindowManagerInternal.AppTransitionListener listener)1422 void unregisterLegacyListener(WindowManagerInternal.AppTransitionListener listener) { 1423 mLegacyListeners.remove(listener); 1424 } 1425 shouldDispatchLegacyListener( WindowManagerInternal.AppTransitionListener listener, int displayId)1426 private static boolean shouldDispatchLegacyListener( 1427 WindowManagerInternal.AppTransitionListener listener, int displayId) { 1428 // INVALID_DISPLAY means that it is a global listener. 1429 return listener.mTargetDisplayId == Display.INVALID_DISPLAY 1430 || listener.mTargetDisplayId == displayId; 1431 } 1432 dispatchLegacyAppTransitionPending(int displayId)1433 void dispatchLegacyAppTransitionPending(int displayId) { 1434 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1435 final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i); 1436 if (shouldDispatchLegacyListener(listener, displayId)) { 1437 listener.onAppTransitionPendingLocked(); 1438 } 1439 } 1440 } 1441 dispatchLegacyAppTransitionStarting(DisplayContent[] participantDisplays, long statusBarTransitionDelay)1442 void dispatchLegacyAppTransitionStarting(DisplayContent[] participantDisplays, 1443 long statusBarTransitionDelay) { 1444 final long now = SystemClock.uptimeMillis(); 1445 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1446 final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i); 1447 for (int j = 0; j < participantDisplays.length; ++j) { 1448 final int displayId = participantDisplays[j].mDisplayId; 1449 if (shouldDispatchLegacyListener(listener, displayId)) { 1450 listener.onAppTransitionStartingLocked( 1451 now + statusBarTransitionDelay, 1452 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); 1453 } 1454 } 1455 } 1456 } 1457 dispatchLegacyAppTransitionFinished(ActivityRecord ar)1458 void dispatchLegacyAppTransitionFinished(ActivityRecord ar) { 1459 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1460 final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i); 1461 if (shouldDispatchLegacyListener(listener, ar.getDisplayId())) { 1462 listener.onAppTransitionFinishedLocked(ar.token); 1463 } 1464 } 1465 } 1466 dispatchLegacyAppTransitionCancelled(ArrayList<DisplayContent> targetDisplays)1467 void dispatchLegacyAppTransitionCancelled(ArrayList<DisplayContent> targetDisplays) { 1468 for (int i = 0; i < targetDisplays.size(); ++i) { 1469 final int displayId = targetDisplays.get(i).mDisplayId; 1470 for (int j = 0; j < mLegacyListeners.size(); ++j) { 1471 final var listener = mLegacyListeners.get(j); 1472 if (shouldDispatchLegacyListener(listener, displayId)) { 1473 listener.onAppTransitionCancelledLocked(false /* keyguardGoingAwayCancelled */); 1474 } 1475 } 1476 } 1477 } 1478 dumpDebugLegacy(ProtoOutputStream proto, long fieldId)1479 void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) { 1480 final long token = proto.start(fieldId); 1481 int state = LEGACY_STATE_IDLE; 1482 if (!mPlayingTransitions.isEmpty()) { 1483 state = LEGACY_STATE_RUNNING; 1484 } else if ((mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) 1485 || mSyncEngine.hasPendingSyncSets()) { 1486 // The transition may not be "ready", but we have a sync-transaction waiting to start. 1487 // Usually the pending transaction is for a transition, so assuming that is the case, 1488 // we can't be IDLE for test purposes. Ideally, we should have a STATE_COLLECTING. 1489 state = LEGACY_STATE_READY; 1490 } 1491 proto.write(AppTransitionProto.APP_TRANSITION_STATE, state); 1492 proto.end(token); 1493 } 1494 1495 /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ queueTransition(Transition transit, OnStartCollect onStartCollect)1496 private void queueTransition(Transition transit, OnStartCollect onStartCollect) { 1497 mQueuedTransitions.add(new QueuedTransition(transit, onStartCollect)); 1498 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1499 "Queueing transition: %s", transit); 1500 } 1501 1502 /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ startCollectOrQueue(Transition transit, OnStartCollect onStartCollect)1503 boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) { 1504 if (!mQueuedTransitions.isEmpty()) { 1505 // Just add to queue since we already have a queue. 1506 queueTransition(transit, onStartCollect); 1507 return false; 1508 } 1509 if (mSyncEngine.hasActiveSync()) { 1510 if (isCollecting()) { 1511 // Check if we can run in parallel here. 1512 if (canStartCollectingNow(transit)) { 1513 // start running in parallel. 1514 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" 1515 + " collecting to waiting.", mCollectingTransition.getSyncId()); 1516 mWaitingTransitions.add(mCollectingTransition); 1517 mCollectingTransition = null; 1518 moveToCollecting(transit); 1519 onStartCollect.onCollectStarted(false /* deferred */); 1520 return true; 1521 } 1522 } else { 1523 Slog.w(TAG, "Ongoing Sync outside of transition."); 1524 } 1525 queueTransition(transit, onStartCollect); 1526 return false; 1527 } 1528 moveToCollecting(transit); 1529 onStartCollect.onCollectStarted(false /* deferred */); 1530 return true; 1531 } 1532 1533 /** 1534 * This will create and start collecting for a transition if possible. If there's no way to 1535 * start collecting for `parallelType` now, then this returns null. 1536 * 1537 * WARNING: ONLY use this if the transition absolutely cannot be deferred! 1538 */ 1539 @Nullable createAndStartCollecting(int type)1540 Transition createAndStartCollecting(int type) { 1541 if (mTransitionPlayers.isEmpty()) { 1542 return null; 1543 } 1544 if (!mQueuedTransitions.isEmpty()) { 1545 // There is a queue, so it's not possible to start immediately 1546 return null; 1547 } 1548 if (mSyncEngine.hasActiveSync()) { 1549 if (isCollecting()) { 1550 // Check if we can run in parallel here. 1551 if (canStartCollectingNow(null /* transit */)) { 1552 // create and collect in parallel. 1553 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" 1554 + " collecting to waiting.", mCollectingTransition.getSyncId()); 1555 mWaitingTransitions.add(mCollectingTransition); 1556 mCollectingTransition = null; 1557 Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); 1558 moveToCollecting(transit); 1559 return transit; 1560 } 1561 } else { 1562 Slog.w(TAG, "Ongoing Sync outside of transition."); 1563 } 1564 return null; 1565 } 1566 Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); 1567 moveToCollecting(transit); 1568 return transit; 1569 } 1570 1571 /** Starts the sync set if there is no pending or active syncs, otherwise enqueue the sync. */ startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync)1572 void startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync) { 1573 if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) { 1574 // Just add to queue since we already have a queue. 1575 mQueuedTransitions.add(new QueuedTransition(syncGroup, 1576 (deferred) -> applySync.accept(true /* deferred */))); 1577 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1578 "Queueing legacy sync-set: %s", syncGroup.mSyncId); 1579 return; 1580 } 1581 mSyncEngine.startSyncSet(syncGroup); 1582 applySync.accept(false /* deferred */); 1583 } 1584 1585 /** 1586 * Instructs the collecting transition to wait until `condition` is met before it is 1587 * considered ready. 1588 */ waitFor(@onNull Transition.ReadyCondition condition)1589 void waitFor(@NonNull Transition.ReadyCondition condition) { 1590 if (mCollectingTransition == null) { 1591 Slog.e(TAG, "No collecting transition available to wait for " + condition); 1592 condition.mTracker = Transition.ReadyTracker.NULL_TRACKER; 1593 return; 1594 } 1595 mCollectingTransition.mReadyTracker.add(condition); 1596 } 1597 1598 interface OnStartCollect { onCollectStarted(boolean deferred)1599 void onCollectStarted(boolean deferred); 1600 } 1601 1602 private class TransitionPlayerRecord { 1603 final ITransitionPlayer mPlayer; 1604 IBinder.DeathRecipient mDeath = null; 1605 private WindowProcessController mPlayerProc; 1606 TransitionPlayerRecord(@onNull ITransitionPlayer player, @Nullable WindowProcessController playerProc)1607 TransitionPlayerRecord(@NonNull ITransitionPlayer player, 1608 @Nullable WindowProcessController playerProc) { 1609 mPlayer = player; 1610 mPlayerProc = playerProc; 1611 try { 1612 linkToDeath(); 1613 } catch (RemoteException e) { 1614 throw new RuntimeException("Unable to set transition player"); 1615 } 1616 } 1617 linkToDeath()1618 private void linkToDeath() throws RemoteException { 1619 // Note: asBinder() can be null if player is same process (likely in a test). 1620 if (mPlayer.asBinder() == null) return; 1621 mDeath = () -> { 1622 synchronized (mAtm.mGlobalLock) { 1623 unregisterTransitionPlayer(mPlayer); 1624 } 1625 }; 1626 mPlayer.asBinder().linkToDeath(mDeath, 0); 1627 } 1628 unlinkToDeath()1629 void unlinkToDeath() { 1630 if (mPlayer.asBinder() == null || mDeath == null) return; 1631 mPlayer.asBinder().unlinkToDeath(mDeath, 0); 1632 mDeath = null; 1633 } 1634 } 1635 1636 /** 1637 * This manages the animating state of processes that are running remote animations for 1638 * {@link #mTransitionPlayerProc}. 1639 */ 1640 static class RemotePlayer { 1641 private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 200; 1642 @GuardedBy("itself") 1643 private final ArrayMap<IBinder, DelegateProcess> mDelegateProcesses = new ArrayMap<>(); 1644 private final ActivityTaskManagerService mAtm; 1645 1646 private class DelegateProcess implements Runnable { 1647 final WindowProcessController mProc; 1648 /** Requires {@link RemotePlayer#reportRunning} to confirm it is really running. */ 1649 boolean mNeedReport; 1650 DelegateProcess(WindowProcessController proc)1651 DelegateProcess(WindowProcessController proc) { 1652 mProc = proc; 1653 } 1654 1655 /** This runs when the remote player doesn't report running in time. */ 1656 @Override run()1657 public void run() { 1658 synchronized (mAtm.mGlobalLockWithoutBoost) { 1659 update(mProc, false /* running */, false /* predict */); 1660 } 1661 } 1662 } 1663 RemotePlayer(ActivityTaskManagerService atm)1664 RemotePlayer(ActivityTaskManagerService atm) { 1665 mAtm = atm; 1666 } 1667 update(@onNull WindowProcessController delegate, boolean running, boolean predict)1668 void update(@NonNull WindowProcessController delegate, boolean running, boolean predict) { 1669 if (!running) { 1670 synchronized (mDelegateProcesses) { 1671 boolean removed = false; 1672 for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) { 1673 if (mDelegateProcesses.valueAt(i).mProc == delegate) { 1674 mDelegateProcesses.removeAt(i); 1675 removed = true; 1676 break; 1677 } 1678 } 1679 if (!removed) return; 1680 } 1681 delegate.setRunningRemoteAnimation(false); 1682 return; 1683 } 1684 if (delegate.isRunningRemoteTransition() || !delegate.hasThread()) return; 1685 delegate.setRunningRemoteAnimation(true); 1686 final DelegateProcess delegateProc = new DelegateProcess(delegate); 1687 // If "predict" is true, that means the remote animation is set from 1688 // ActivityOptions#makeRemoteAnimation(). But it is still up to shell side to decide 1689 // whether to use the remote animation, so there is a timeout to cancel the prediction 1690 // if the remote animation doesn't happen. 1691 if (predict) { 1692 delegateProc.mNeedReport = true; 1693 mAtm.mH.postDelayed(delegateProc, REPORT_RUNNING_GRACE_PERIOD_MS); 1694 } 1695 synchronized (mDelegateProcesses) { 1696 mDelegateProcesses.put(delegate.getThread().asBinder(), delegateProc); 1697 } 1698 } 1699 clear()1700 void clear() { 1701 synchronized (mDelegateProcesses) { 1702 for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) { 1703 mDelegateProcesses.valueAt(i).mProc.setRunningRemoteAnimation(false); 1704 } 1705 mDelegateProcesses.clear(); 1706 } 1707 } 1708 1709 /** Returns {@code true} if the app is known to be running remote transition. */ reportRunning(@onNull IApplicationThread appThread)1710 boolean reportRunning(@NonNull IApplicationThread appThread) { 1711 final DelegateProcess delegate; 1712 synchronized (mDelegateProcesses) { 1713 delegate = mDelegateProcesses.get(appThread.asBinder()); 1714 if (delegate != null && delegate.mNeedReport) { 1715 // It was predicted to run remote transition. Now it is really requesting so 1716 // remove the timeout of restoration. 1717 delegate.mNeedReport = false; 1718 mAtm.mH.removeCallbacks(delegate); 1719 } 1720 } 1721 return delegate != null; 1722 } 1723 } 1724 1725 /** 1726 * Data-class to store recorded events/info for a transition. This allows us to defer the 1727 * actual logging until the system isn't busy. This also records some common metrics to see 1728 * delays at-a-glance. 1729 * 1730 * Beside `mCreateWallTimeMs`, all times are elapsed times and will all be reported relative 1731 * to when the transition was created. 1732 */ 1733 static class Logger implements Runnable { 1734 long mCreateWallTimeMs; 1735 long mCreateTimeNs; 1736 long mRequestTimeNs; 1737 long mCollectTimeNs; 1738 long mStartTimeNs; 1739 long mReadyTimeNs; 1740 long mSendTimeNs; 1741 long mTransactionCommitTimeNs; 1742 long mFinishTimeNs; 1743 long mAbortTimeNs; 1744 TransitionRequestInfo mRequest; 1745 WindowContainerTransaction mStartWCT; 1746 int mSyncId; 1747 TransitionInfo mInfo; 1748 buildOnSendLog()1749 private String buildOnSendLog() { 1750 StringBuilder sb = new StringBuilder("Sent Transition (#").append(mSyncId) 1751 .append(") createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs)); 1752 if (mRequest != null) { 1753 sb.append(" via request=").append(mRequest); 1754 } 1755 return sb.toString(); 1756 } 1757 logOnSendAsync(Handler handler)1758 void logOnSendAsync(Handler handler) { 1759 handler.post(this); 1760 } 1761 1762 @Override run()1763 public void run() { 1764 try { 1765 logOnSend(); 1766 } catch (Exception e) { 1767 // In case TransitionRequestInfo#toString() accesses window container with race. 1768 Slog.w(TAG, "Failed to log transition", e); 1769 } 1770 } 1771 logOnSend()1772 void logOnSend() { 1773 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog()); 1774 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1775 " startWCT=%s", mStartWCT); 1776 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s", 1777 mInfo.toString(" " /* prefix */)); 1778 } 1779 toMsString(long nanos)1780 private static String toMsString(long nanos) { 1781 return ((double) Math.round((double) nanos / 1000) / 1000) + "ms"; 1782 } 1783 buildOnFinishLog()1784 private String buildOnFinishLog() { 1785 StringBuilder sb = new StringBuilder("Finish Transition (#").append(mSyncId) 1786 .append("): created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs)); 1787 sb.append(" collect-started=").append(toMsString(mCollectTimeNs - mCreateTimeNs)); 1788 if (mRequestTimeNs != 0) { 1789 sb.append(" request-sent=").append(toMsString(mRequestTimeNs - mCreateTimeNs)); 1790 } 1791 sb.append(" started=").append(toMsString(mStartTimeNs - mCreateTimeNs)); 1792 sb.append(" ready=").append(toMsString(mReadyTimeNs - mCreateTimeNs)); 1793 sb.append(" sent=").append(toMsString(mSendTimeNs - mCreateTimeNs)); 1794 if (mTransactionCommitTimeNs != 0) { 1795 sb.append(" commit=").append(toMsString(mTransactionCommitTimeNs - mSendTimeNs)); 1796 } 1797 sb.append(" finished=").append(toMsString(mFinishTimeNs - mCreateTimeNs)); 1798 return sb.toString(); 1799 } 1800 logOnFinish()1801 void logOnFinish() { 1802 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnFinishLog()); 1803 } 1804 } 1805 1806 static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub { 1807 private final ArrayMap<IBinder, LongConsumer> mMetricConsumers = new ArrayMap<>(); 1808 associate(IBinder transitionToken, LongConsumer consumer)1809 void associate(IBinder transitionToken, LongConsumer consumer) { 1810 synchronized (mMetricConsumers) { 1811 mMetricConsumers.put(transitionToken, consumer); 1812 } 1813 } 1814 1815 @Override reportAnimationStart(IBinder transitionToken, long startTime)1816 public void reportAnimationStart(IBinder transitionToken, long startTime) { 1817 final LongConsumer c; 1818 synchronized (mMetricConsumers) { 1819 if (mMetricConsumers.isEmpty()) return; 1820 c = mMetricConsumers.remove(transitionToken); 1821 } 1822 if (c != null) { 1823 c.accept(startTime); 1824 } 1825 } 1826 } 1827 1828 class Lock { 1829 private int mTransitionWaiters = 0; runWhenIdle(long timeout, Runnable r)1830 void runWhenIdle(long timeout, Runnable r) { 1831 synchronized (mAtm.mGlobalLock) { 1832 if (!inTransition()) { 1833 r.run(); 1834 return; 1835 } 1836 mTransitionWaiters += 1; 1837 } 1838 final long startTime = SystemClock.uptimeMillis(); 1839 final long endTime = startTime + timeout; 1840 while (true) { 1841 synchronized (mAtm.mGlobalLock) { 1842 if (!inTransition() || SystemClock.uptimeMillis() > endTime) { 1843 mTransitionWaiters -= 1; 1844 r.run(); 1845 return; 1846 } 1847 } 1848 synchronized (this) { 1849 try { 1850 this.wait(timeout); 1851 } catch (InterruptedException e) { 1852 return; 1853 } 1854 } 1855 } 1856 } 1857 doNotifyLocked()1858 void doNotifyLocked() { 1859 synchronized (this) { 1860 if (mTransitionWaiters > 0) { 1861 this.notifyAll(); 1862 } 1863 } 1864 } 1865 } 1866 } 1867