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