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 onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller)954 void onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller) { 955 final boolean isPlaying = !mPlayingTransitions.isEmpty(); 956 Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying 957 + " caller=" + caller); 958 if (!isPlaying) { 959 enforceSurfaceVisible(wc); 960 return; 961 } 962 // Update surface visibility after the playing transitions are finished, so the last 963 // visibility won't be replaced by the finish transaction of transition. 964 mStateValidators.add(() -> { 965 if (wc.isVisibleRequested()) { 966 enforceSurfaceVisible(wc); 967 } 968 }); 969 } 970 enforceSurfaceVisible(WindowContainer<?> wc)971 private void enforceSurfaceVisible(WindowContainer<?> wc) { 972 if (wc.mSurfaceControl == null) return; 973 wc.getSyncTransaction().show(wc.mSurfaceControl); 974 final ActivityRecord ar = wc.asActivityRecord(); 975 if (ar != null) { 976 ar.mLastSurfaceShowing = true; 977 } 978 // Force showing the parents because they may be hidden by previous transition. 979 for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent; 980 p = p.getParent()) { 981 if (p.mSurfaceControl != null) { 982 p.getSyncTransaction().show(p.mSurfaceControl); 983 final Task task = p.asTask(); 984 if (task != null) { 985 task.mLastSurfaceShowing = true; 986 } 987 } 988 } 989 wc.scheduleAnimation(); 990 } 991 992 /** 993 * Called when the transition has a complete set of participants for its operation. In other 994 * words, it is when the transition is "ready" but is still waiting for participants to draw. 995 */ onTransitionPopulated(Transition transition)996 void onTransitionPopulated(Transition transition) { 997 tryStartCollectFromQueue(); 998 } 999 canStartCollectingNow(@ullable Transition queued)1000 private boolean canStartCollectingNow(@Nullable Transition queued) { 1001 if (mCollectingTransition == null) return true; 1002 // Population (collect until ready) is still serialized, so always wait for that. 1003 if (!mCollectingTransition.isPopulated()) return false; 1004 // Check if queued *can* be independent with all collecting/waiting transitions. 1005 if (!getCanBeIndependent(mCollectingTransition, queued)) return false; 1006 for (int i = 0; i < mWaitingTransitions.size(); ++i) { 1007 if (!getCanBeIndependent(mWaitingTransitions.get(i), queued)) return false; 1008 } 1009 return true; 1010 } 1011 tryStartCollectFromQueue()1012 void tryStartCollectFromQueue() { 1013 if (mQueuedTransitions.isEmpty()) return; 1014 // Only need to try the next one since, even when transition can collect in parallel, 1015 // they still need to serialize on readiness. 1016 final QueuedTransition queued = mQueuedTransitions.get(0); 1017 if (mCollectingTransition != null) { 1018 // If it's a legacy sync, then it needs to wait until there is no collecting transition. 1019 if (queued.mTransition == null) return; 1020 if (!canStartCollectingNow(queued.mTransition)) return; 1021 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from collecting" 1022 + " to waiting.", mCollectingTransition.getSyncId()); 1023 mWaitingTransitions.add(mCollectingTransition); 1024 mCollectingTransition = null; 1025 } else if (mSyncEngine.hasActiveSync()) { 1026 // A legacy transition is on-going, so we must wait. 1027 return; 1028 } 1029 mQueuedTransitions.remove(0); 1030 // This needs to happen immediately to prevent another sync from claiming the syncset 1031 // out-of-order (moveToCollecting calls startSyncSet) 1032 if (queued.mTransition != null) { 1033 moveToCollecting(queued.mTransition); 1034 } else { 1035 // legacy sync 1036 mSyncEngine.startSyncSet(queued.mLegacySync); 1037 } 1038 // Post this so that the now-playing transition logic isn't interrupted. 1039 mAtm.mH.post(() -> { 1040 synchronized (mAtm.mGlobalLock) { 1041 queued.mOnStartCollect.onCollectStarted(true /* deferred */); 1042 } 1043 }); 1044 } 1045 moveToPlaying(Transition transition)1046 void moveToPlaying(Transition transition) { 1047 if (transition == mCollectingTransition) { 1048 mCollectingTransition = null; 1049 if (!mWaitingTransitions.isEmpty()) { 1050 mCollectingTransition = mWaitingTransitions.remove(0); 1051 } 1052 if (mCollectingTransition == null) { 1053 // nothing collecting anymore, so clear order records. 1054 mLatestOnTopTasksReported.clear(); 1055 } 1056 } else { 1057 if (!mWaitingTransitions.remove(transition)) { 1058 throw new IllegalStateException("Trying to move non-collecting transition to" 1059 + "playing " + transition.getSyncId()); 1060 } 1061 } 1062 mPlayingTransitions.add(transition); 1063 updateRunningRemoteAnimation(transition, true /* isPlaying */); 1064 // Sync engine should become idle after this, so the idle listener will check the queue. 1065 } 1066 1067 /** 1068 * Checks if the `queued` transition has the potential to run independently of the 1069 * `collecting` transition. It may still ultimately block in sync-engine or become dependent 1070 * in {@link #getIsIndependent} later. 1071 */ getCanBeIndependent(Transition collecting, @Nullable Transition queued)1072 boolean getCanBeIndependent(Transition collecting, @Nullable Transition queued) { 1073 // For tests 1074 if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL 1075 && collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { 1076 return true; 1077 } 1078 // For recents 1079 if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1080 if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1081 // Must serialize with itself. 1082 return false; 1083 } 1084 // allow this if `collecting` only has activities 1085 for (int i = 0; i < collecting.mParticipants.size(); ++i) { 1086 final WindowContainer wc = collecting.mParticipants.valueAt(i); 1087 final ActivityRecord ar = wc.asActivityRecord(); 1088 if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { 1089 // Is task or above, so can't be independent 1090 return false; 1091 } 1092 if (ar != null && ar.isActivityTypeHomeOrRecents()) { 1093 // It's a recents or home type, so it conflicts. 1094 return false; 1095 } 1096 } 1097 return true; 1098 } else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1099 // We can collect simultaneously with recents if it is populated. This is because 1100 // we know that recents will not collect/trampoline any more stuff. If anything in the 1101 // queued transition overlaps, it will end up just waiting in sync-queue anyways. 1102 return true; 1103 } 1104 return false; 1105 } 1106 1107 /** 1108 * Checks if `incoming` transition can run independently of `running` transition assuming that 1109 * `running` is playing based on its current state. 1110 */ getIsIndependent(Transition running, Transition incoming)1111 static boolean getIsIndependent(Transition running, Transition incoming) { 1112 // For tests 1113 if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL 1114 && incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { 1115 return true; 1116 } 1117 // For now there's only one mutually-independent pair: an all activity-level transition and 1118 // a transient-launch where none of the activities are part of the transient-launch task, 1119 // so the following logic is hard-coded specifically for this. 1120 // Also, we currently restrict valid transient-launches to just recents. 1121 final Transition recents; 1122 final Transition other; 1123 if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS 1124 && running.hasTransientLaunch()) { 1125 if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { 1126 // Recents can't be independent from itself. 1127 return false; 1128 } 1129 recents = running; 1130 other = incoming; 1131 } else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS 1132 && incoming.hasTransientLaunch()) { 1133 recents = incoming; 1134 other = running; 1135 } else { 1136 return false; 1137 } 1138 // Check against *targets* because that is the post-promotion set of containers that are 1139 // actually animating. 1140 for (int i = 0; i < other.mTargets.size(); ++i) { 1141 final WindowContainer wc = other.mTargets.get(i).mContainer; 1142 final ActivityRecord ar = wc.asActivityRecord(); 1143 if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { 1144 // Is task or above, so for now don't let them be independent. 1145 return false; 1146 } 1147 if (ar != null && recents.isTransientLaunch(ar)) { 1148 // Change overlaps with recents, so serialize. 1149 return false; 1150 } 1151 } 1152 return true; 1153 } 1154 assignTrack(Transition transition, TransitionInfo info)1155 void assignTrack(Transition transition, TransitionInfo info) { 1156 int track = -1; 1157 boolean sync = false; 1158 for (int i = 0; i < mPlayingTransitions.size(); ++i) { 1159 // ignore ourself obviously 1160 if (mPlayingTransitions.get(i) == transition) continue; 1161 if (getIsIndependent(mPlayingTransitions.get(i), transition)) continue; 1162 if (track >= 0) { 1163 // At this point, transition overlaps with multiple tracks, so just wait for 1164 // everything 1165 sync = true; 1166 break; 1167 } 1168 track = mPlayingTransitions.get(i).mAnimationTrack; 1169 } 1170 if (sync) { 1171 track = 0; 1172 } 1173 if (track < 0) { 1174 // Didn't overlap with anything, so give it its own track 1175 track = mTrackCount; 1176 if (track > 0) { 1177 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Playing #%d in parallel on " 1178 + "track #%d", transition.getSyncId(), track); 1179 } 1180 } 1181 transition.mAnimationTrack = track; 1182 info.setTrack(track); 1183 mTrackCount = Math.max(mTrackCount, track + 1); 1184 if (sync && mTrackCount > 1) { 1185 // If there are >1 tracks, mark as sync so that all tracks finish. 1186 info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC); 1187 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.", 1188 transition.getSyncId()); 1189 } 1190 } 1191 updateAnimatingState(SurfaceControl.Transaction t)1192 void updateAnimatingState(SurfaceControl.Transaction t) { 1193 final boolean animatingState = !mPlayingTransitions.isEmpty() 1194 || (mCollectingTransition != null && mCollectingTransition.isStarted()); 1195 if (animatingState && !mAnimatingState) { 1196 t.setEarlyWakeupStart(); 1197 // Usually transitions put quite a load onto the system already (with all the things 1198 // happening in app), so pause task snapshot persisting to not increase the load. 1199 mSnapshotController.setPause(true); 1200 mAnimatingState = true; 1201 Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */); 1202 } else if (!animatingState && mAnimatingState) { 1203 t.setEarlyWakeupEnd(); 1204 mAtm.mWindowManager.scheduleAnimationLocked(); 1205 mSnapshotController.setPause(false); 1206 mAnimatingState = false; 1207 Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */); 1208 } 1209 } 1210 1211 /** Updates the process state of animation player. */ updateRunningRemoteAnimation(Transition transition, boolean isPlaying)1212 private void updateRunningRemoteAnimation(Transition transition, boolean isPlaying) { 1213 if (mTransitionPlayerProc == null) return; 1214 if (isPlaying) { 1215 mTransitionPlayerProc.setRunningRemoteAnimation(true); 1216 } else if (mPlayingTransitions.isEmpty()) { 1217 mTransitionPlayerProc.setRunningRemoteAnimation(false); 1218 mRemotePlayer.clear(); 1219 return; 1220 } 1221 final IApplicationThread appThread = transition.getRemoteAnimationApp(); 1222 if (appThread == null || appThread == mTransitionPlayerProc.getThread()) return; 1223 final WindowProcessController delegate = mAtm.getProcessController(appThread); 1224 if (delegate == null) return; 1225 mRemotePlayer.update(delegate, isPlaying, true /* predict */); 1226 } 1227 1228 /** Called when a transition is aborted. This should only be called by {@link Transition} */ onAbort(Transition transition)1229 void onAbort(Transition transition) { 1230 if (transition != mCollectingTransition) { 1231 int waitingIdx = mWaitingTransitions.indexOf(transition); 1232 if (waitingIdx < 0) { 1233 throw new IllegalStateException("Too late for abort."); 1234 } 1235 mWaitingTransitions.remove(waitingIdx); 1236 } else { 1237 mCollectingTransition = null; 1238 if (!mWaitingTransitions.isEmpty()) { 1239 mCollectingTransition = mWaitingTransitions.remove(0); 1240 } 1241 if (mCollectingTransition == null) { 1242 // nothing collecting anymore, so clear order records. 1243 mLatestOnTopTasksReported.clear(); 1244 } 1245 } 1246 // This is called during Transition.abort whose codepath will eventually check the queue 1247 // via sync-engine idle. 1248 } 1249 1250 /** 1251 * Record that the launch of {@param activity} is transient (meaning its lifecycle is currently 1252 * tied to the transition). 1253 * @param restoreBelowTask If non-null, the activity's task will be ordered right below this 1254 * task if requested. 1255 */ setTransientLaunch(@onNull ActivityRecord activity, @Nullable Task restoreBelowTask)1256 void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelowTask) { 1257 if (mCollectingTransition == null) return; 1258 mCollectingTransition.setTransientLaunch(activity, restoreBelowTask); 1259 1260 // TODO(b/188669821): Remove once legacy recents behavior is moved to shell. 1261 // Also interpret HOME transient launch as recents 1262 if (activity.isActivityTypeHomeOrRecents()) { 1263 mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS); 1264 // When starting recents animation, we assume the recents activity is behind the app 1265 // task and should not affect system bar appearance, 1266 // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing 1267 // the gesture threshold. 1268 activity.getTask().setCanAffectSystemUiFlags(false); 1269 } 1270 } 1271 1272 /** @see Transition#setCanPipOnFinish */ setCanPipOnFinish(boolean canPipOnFinish)1273 void setCanPipOnFinish(boolean canPipOnFinish) { 1274 if (mCollectingTransition == null) return; 1275 mCollectingTransition.setCanPipOnFinish(canPipOnFinish); 1276 } 1277 legacyDetachNavigationBarFromApp(@onNull IBinder token)1278 void legacyDetachNavigationBarFromApp(@NonNull IBinder token) { 1279 final Transition transition = Transition.fromBinder(token); 1280 if (transition == null || !mPlayingTransitions.contains(transition)) { 1281 Slog.e(TAG, "Transition isn't playing: " + token); 1282 return; 1283 } 1284 transition.legacyRestoreNavigationBarFromApp(); 1285 } 1286 registerLegacyListener(WindowManagerInternal.AppTransitionListener listener)1287 void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) { 1288 mLegacyListeners.add(listener); 1289 } 1290 unregisterLegacyListener(WindowManagerInternal.AppTransitionListener listener)1291 void unregisterLegacyListener(WindowManagerInternal.AppTransitionListener listener) { 1292 mLegacyListeners.remove(listener); 1293 } 1294 dispatchLegacyAppTransitionPending()1295 void dispatchLegacyAppTransitionPending() { 1296 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1297 mLegacyListeners.get(i).onAppTransitionPendingLocked(); 1298 } 1299 } 1300 dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay)1301 void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) { 1302 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1303 // TODO(shell-transitions): handle (un)occlude transition. 1304 mLegacyListeners.get(i).onAppTransitionStartingLocked( 1305 SystemClock.uptimeMillis() + statusBarTransitionDelay, 1306 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); 1307 } 1308 } 1309 dispatchLegacyAppTransitionFinished(ActivityRecord ar)1310 void dispatchLegacyAppTransitionFinished(ActivityRecord ar) { 1311 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1312 mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token); 1313 } 1314 } 1315 dispatchLegacyAppTransitionCancelled()1316 void dispatchLegacyAppTransitionCancelled() { 1317 for (int i = 0; i < mLegacyListeners.size(); ++i) { 1318 mLegacyListeners.get(i).onAppTransitionCancelledLocked( 1319 false /* keyguardGoingAwayCancelled */); 1320 } 1321 } 1322 dumpDebugLegacy(ProtoOutputStream proto, long fieldId)1323 void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) { 1324 final long token = proto.start(fieldId); 1325 int state = LEGACY_STATE_IDLE; 1326 if (!mPlayingTransitions.isEmpty()) { 1327 state = LEGACY_STATE_RUNNING; 1328 } else if ((mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) 1329 || mSyncEngine.hasPendingSyncSets()) { 1330 // The transition may not be "ready", but we have a sync-transaction waiting to start. 1331 // Usually the pending transaction is for a transition, so assuming that is the case, 1332 // we can't be IDLE for test purposes. Ideally, we should have a STATE_COLLECTING. 1333 state = LEGACY_STATE_READY; 1334 } 1335 proto.write(AppTransitionProto.APP_TRANSITION_STATE, state); 1336 proto.end(token); 1337 } 1338 1339 /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ queueTransition(Transition transit, OnStartCollect onStartCollect)1340 private void queueTransition(Transition transit, OnStartCollect onStartCollect) { 1341 mQueuedTransitions.add(new QueuedTransition(transit, onStartCollect)); 1342 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1343 "Queueing transition: %s", transit); 1344 } 1345 1346 /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ startCollectOrQueue(Transition transit, OnStartCollect onStartCollect)1347 boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) { 1348 if (!mQueuedTransitions.isEmpty()) { 1349 // Just add to queue since we already have a queue. 1350 queueTransition(transit, onStartCollect); 1351 return false; 1352 } 1353 if (mSyncEngine.hasActiveSync()) { 1354 if (isCollecting()) { 1355 // Check if we can run in parallel here. 1356 if (canStartCollectingNow(transit)) { 1357 // start running in parallel. 1358 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" 1359 + " collecting to waiting.", mCollectingTransition.getSyncId()); 1360 mWaitingTransitions.add(mCollectingTransition); 1361 mCollectingTransition = null; 1362 moveToCollecting(transit); 1363 onStartCollect.onCollectStarted(false /* deferred */); 1364 return true; 1365 } 1366 } else { 1367 Slog.w(TAG, "Ongoing Sync outside of transition."); 1368 } 1369 queueTransition(transit, onStartCollect); 1370 return false; 1371 } 1372 moveToCollecting(transit); 1373 onStartCollect.onCollectStarted(false /* deferred */); 1374 return true; 1375 } 1376 1377 /** 1378 * This will create and start collecting for a transition if possible. If there's no way to 1379 * start collecting for `parallelType` now, then this returns null. 1380 * 1381 * WARNING: ONLY use this if the transition absolutely cannot be deferred! 1382 */ 1383 @NonNull createAndStartCollecting(int type)1384 Transition createAndStartCollecting(int type) { 1385 if (mTransitionPlayer == null) { 1386 return null; 1387 } 1388 if (!mQueuedTransitions.isEmpty()) { 1389 // There is a queue, so it's not possible to start immediately 1390 return null; 1391 } 1392 if (mSyncEngine.hasActiveSync()) { 1393 if (isCollecting()) { 1394 // Check if we can run in parallel here. 1395 if (canStartCollectingNow(null /* transit */)) { 1396 // create and collect in parallel. 1397 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" 1398 + " collecting to waiting.", mCollectingTransition.getSyncId()); 1399 mWaitingTransitions.add(mCollectingTransition); 1400 mCollectingTransition = null; 1401 Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); 1402 moveToCollecting(transit); 1403 return transit; 1404 } 1405 } else { 1406 Slog.w(TAG, "Ongoing Sync outside of transition."); 1407 } 1408 return null; 1409 } 1410 Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); 1411 moveToCollecting(transit); 1412 return transit; 1413 } 1414 1415 /** Starts the sync set if there is no pending or active syncs, otherwise enqueue the sync. */ startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync)1416 void startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync) { 1417 if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) { 1418 // Just add to queue since we already have a queue. 1419 mQueuedTransitions.add(new QueuedTransition(syncGroup, 1420 (deferred) -> applySync.accept(true /* deferred */))); 1421 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, 1422 "Queueing legacy sync-set: %s", syncGroup.mSyncId); 1423 return; 1424 } 1425 mSyncEngine.startSyncSet(syncGroup); 1426 applySync.accept(false /* deferred */); 1427 } 1428 1429 interface OnStartCollect { onCollectStarted(boolean deferred)1430 void onCollectStarted(boolean deferred); 1431 } 1432 1433 /** 1434 * This manages the animating state of processes that are running remote animations for 1435 * {@link #mTransitionPlayerProc}. 1436 */ 1437 static class RemotePlayer { 1438 private static final long REPORT_RUNNING_GRACE_PERIOD_MS = 100; 1439 @GuardedBy("itself") 1440 private final ArrayMap<IBinder, DelegateProcess> mDelegateProcesses = new ArrayMap<>(); 1441 private final ActivityTaskManagerService mAtm; 1442 1443 private class DelegateProcess implements Runnable { 1444 final WindowProcessController mProc; 1445 /** Requires {@link RemotePlayer#reportRunning} to confirm it is really running. */ 1446 boolean mNeedReport; 1447 DelegateProcess(WindowProcessController proc)1448 DelegateProcess(WindowProcessController proc) { 1449 mProc = proc; 1450 } 1451 1452 /** This runs when the remote player doesn't report running in time. */ 1453 @Override run()1454 public void run() { 1455 synchronized (mAtm.mGlobalLockWithoutBoost) { 1456 update(mProc, false /* running */, false /* predict */); 1457 } 1458 } 1459 } 1460 RemotePlayer(ActivityTaskManagerService atm)1461 RemotePlayer(ActivityTaskManagerService atm) { 1462 mAtm = atm; 1463 } 1464 update(@onNull WindowProcessController delegate, boolean running, boolean predict)1465 void update(@NonNull WindowProcessController delegate, boolean running, boolean predict) { 1466 if (!running) { 1467 synchronized (mDelegateProcesses) { 1468 boolean removed = false; 1469 for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) { 1470 if (mDelegateProcesses.valueAt(i).mProc == delegate) { 1471 mDelegateProcesses.removeAt(i); 1472 removed = true; 1473 break; 1474 } 1475 } 1476 if (!removed) return; 1477 } 1478 delegate.setRunningRemoteAnimation(false); 1479 return; 1480 } 1481 if (delegate.isRunningRemoteTransition() || !delegate.hasThread()) return; 1482 delegate.setRunningRemoteAnimation(true); 1483 final DelegateProcess delegateProc = new DelegateProcess(delegate); 1484 // If "predict" is true, that means the remote animation is set from 1485 // ActivityOptions#makeRemoteAnimation(). But it is still up to shell side to decide 1486 // whether to use the remote animation, so there is a timeout to cancel the prediction 1487 // if the remote animation doesn't happen. 1488 if (predict) { 1489 delegateProc.mNeedReport = true; 1490 mAtm.mH.postDelayed(delegateProc, REPORT_RUNNING_GRACE_PERIOD_MS); 1491 } 1492 synchronized (mDelegateProcesses) { 1493 mDelegateProcesses.put(delegate.getThread().asBinder(), delegateProc); 1494 } 1495 } 1496 clear()1497 void clear() { 1498 synchronized (mDelegateProcesses) { 1499 for (int i = mDelegateProcesses.size() - 1; i >= 0; i--) { 1500 mDelegateProcesses.valueAt(i).mProc.setRunningRemoteAnimation(false); 1501 } 1502 mDelegateProcesses.clear(); 1503 } 1504 } 1505 1506 /** Returns {@code true} if the app is known to be running remote transition. */ reportRunning(@onNull IApplicationThread appThread)1507 boolean reportRunning(@NonNull IApplicationThread appThread) { 1508 final DelegateProcess delegate; 1509 synchronized (mDelegateProcesses) { 1510 delegate = mDelegateProcesses.get(appThread.asBinder()); 1511 if (delegate != null && delegate.mNeedReport) { 1512 // It was predicted to run remote transition. Now it is really requesting so 1513 // remove the timeout of restoration. 1514 delegate.mNeedReport = false; 1515 mAtm.mH.removeCallbacks(delegate); 1516 } 1517 } 1518 return delegate != null; 1519 } 1520 } 1521 1522 /** 1523 * Data-class to store recorded events/info for a transition. This allows us to defer the 1524 * actual logging until the system isn't busy. This also records some common metrics to see 1525 * delays at-a-glance. 1526 * 1527 * Beside `mCreateWallTimeMs`, all times are elapsed times and will all be reported relative 1528 * to when the transition was created. 1529 */ 1530 static class Logger { 1531 long mCreateWallTimeMs; 1532 long mCreateTimeNs; 1533 long mRequestTimeNs; 1534 long mCollectTimeNs; 1535 long mStartTimeNs; 1536 long mReadyTimeNs; 1537 long mSendTimeNs; 1538 long mFinishTimeNs; 1539 long mAbortTimeNs; 1540 TransitionRequestInfo mRequest; 1541 WindowContainerTransaction mStartWCT; 1542 int mSyncId; 1543 TransitionInfo mInfo; 1544 buildOnSendLog()1545 private String buildOnSendLog() { 1546 StringBuilder sb = new StringBuilder("Sent Transition #").append(mSyncId) 1547 .append(" createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs)); 1548 if (mRequest != null) { 1549 sb.append(" via request=").append(mRequest); 1550 } 1551 return sb.toString(); 1552 } 1553 logOnSend()1554 void logOnSend() { 1555 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog()); 1556 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " startWCT=%s", mStartWCT); 1557 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s", mInfo); 1558 } 1559 toMsString(long nanos)1560 private static String toMsString(long nanos) { 1561 return ((double) Math.round((double) nanos / 1000) / 1000) + "ms"; 1562 } 1563 buildOnFinishLog()1564 private String buildOnFinishLog() { 1565 StringBuilder sb = new StringBuilder("Finish Transition #").append(mSyncId) 1566 .append(": created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs)); 1567 sb.append(" collect-started=").append(toMsString(mCollectTimeNs - mCreateTimeNs)); 1568 if (mRequestTimeNs != 0) { 1569 sb.append(" request-sent=").append(toMsString(mRequestTimeNs - mCreateTimeNs)); 1570 } 1571 sb.append(" started=").append(toMsString(mStartTimeNs - mCreateTimeNs)); 1572 sb.append(" ready=").append(toMsString(mReadyTimeNs - mCreateTimeNs)); 1573 sb.append(" sent=").append(toMsString(mSendTimeNs - mCreateTimeNs)); 1574 sb.append(" finished=").append(toMsString(mFinishTimeNs - mCreateTimeNs)); 1575 return sb.toString(); 1576 } 1577 logOnFinish()1578 void logOnFinish() { 1579 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnFinishLog()); 1580 } 1581 } 1582 1583 static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub { 1584 private final ArrayMap<IBinder, LongConsumer> mMetricConsumers = new ArrayMap<>(); 1585 associate(IBinder transitionToken, LongConsumer consumer)1586 void associate(IBinder transitionToken, LongConsumer consumer) { 1587 synchronized (mMetricConsumers) { 1588 mMetricConsumers.put(transitionToken, consumer); 1589 } 1590 } 1591 1592 @Override reportAnimationStart(IBinder transitionToken, long startTime)1593 public void reportAnimationStart(IBinder transitionToken, long startTime) { 1594 final LongConsumer c; 1595 synchronized (mMetricConsumers) { 1596 if (mMetricConsumers.isEmpty()) return; 1597 c = mMetricConsumers.remove(transitionToken); 1598 } 1599 if (c != null) { 1600 c.accept(startTime); 1601 } 1602 } 1603 } 1604 1605 class Lock { 1606 private int mTransitionWaiters = 0; runWhenIdle(long timeout, Runnable r)1607 void runWhenIdle(long timeout, Runnable r) { 1608 synchronized (mAtm.mGlobalLock) { 1609 if (!inTransition()) { 1610 r.run(); 1611 return; 1612 } 1613 mTransitionWaiters += 1; 1614 } 1615 final long startTime = SystemClock.uptimeMillis(); 1616 final long endTime = startTime + timeout; 1617 while (true) { 1618 synchronized (mAtm.mGlobalLock) { 1619 if (!inTransition() || SystemClock.uptimeMillis() > endTime) { 1620 mTransitionWaiters -= 1; 1621 r.run(); 1622 return; 1623 } 1624 } 1625 synchronized (this) { 1626 try { 1627 this.wait(timeout); 1628 } catch (InterruptedException e) { 1629 return; 1630 } 1631 } 1632 } 1633 } 1634 doNotifyLocked()1635 void doNotifyLocked() { 1636 synchronized (this) { 1637 if (mTransitionWaiters > 0) { 1638 this.notifyAll(); 1639 } 1640 } 1641 } 1642 } 1643 } 1644