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