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