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