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