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