1 /*
2  * Copyright (C) 2018 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.TRANSIT_CHANGE;
20 import static android.view.WindowManager.TRANSIT_CLOSE;
21 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
22 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
23 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
24 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
25 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
26 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
27 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
28 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
29 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
30 import static android.view.WindowManager.TRANSIT_NONE;
31 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
32 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
33 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
34 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
35 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
36 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
37 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
38 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
39 import static android.view.WindowManager.TRANSIT_OLD_NONE;
40 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
41 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
42 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
43 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
44 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
45 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
46 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
47 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
48 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
49 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
50 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
51 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
52 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
53 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
54 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
55 import static android.view.WindowManager.TRANSIT_OPEN;
56 import static android.view.WindowManager.TRANSIT_RELAUNCH;
57 import static android.view.WindowManager.TRANSIT_TO_BACK;
58 import static android.view.WindowManager.TRANSIT_TO_FRONT;
59 
60 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
61 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
62 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
63 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
64 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
65 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
66 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
67 import static com.android.server.wm.AppTransition.isNormalTransit;
68 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp;
69 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit;
70 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
71 import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
72 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
73 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
74 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
75 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
76 
77 import android.annotation.IntDef;
78 import android.annotation.Nullable;
79 import android.os.Trace;
80 import android.util.ArrayMap;
81 import android.util.ArraySet;
82 import android.util.Slog;
83 import android.view.Display;
84 import android.view.RemoteAnimationAdapter;
85 import android.view.RemoteAnimationDefinition;
86 import android.view.WindowManager;
87 import android.view.WindowManager.LayoutParams;
88 import android.view.WindowManager.TransitionFlags;
89 import android.view.WindowManager.TransitionOldType;
90 import android.view.WindowManager.TransitionType;
91 import android.view.animation.Animation;
92 import android.window.ITaskFragmentOrganizer;
93 
94 import com.android.internal.annotations.VisibleForTesting;
95 import com.android.internal.protolog.common.ProtoLog;
96 
97 import java.lang.annotation.Retention;
98 import java.lang.annotation.RetentionPolicy;
99 import java.util.ArrayList;
100 import java.util.LinkedList;
101 import java.util.function.Predicate;
102 
103 /**
104  * Checks for app transition readiness, resolves animation attributes and performs visibility
105  * change for apps that animate as part of an app transition.
106  */
107 public class AppTransitionController {
108     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM;
109     private final WindowManagerService mService;
110     private final DisplayContent mDisplayContent;
111     private final WallpaperController mWallpaperControllerLocked;
112     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
113     private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
114 
115     private static final int TYPE_NONE = 0;
116     private static final int TYPE_ACTIVITY = 1;
117     private static final int TYPE_TASK_FRAGMENT = 2;
118     private static final int TYPE_TASK = 3;
119 
120     @IntDef(prefix = { "TYPE_" }, value = {
121             TYPE_NONE,
122             TYPE_ACTIVITY,
123             TYPE_TASK_FRAGMENT,
124             TYPE_TASK
125     })
126     @Retention(RetentionPolicy.SOURCE)
127     @interface TransitContainerType {}
128 
129     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
130     private final ArrayList<WindowContainer> mTempTransitionWindows = new ArrayList<>();
131 
AppTransitionController(WindowManagerService service, DisplayContent displayContent)132     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
133         mService = service;
134         mDisplayContent = displayContent;
135         mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
136     }
137 
registerRemoteAnimations(RemoteAnimationDefinition definition)138     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
139         mRemoteAnimationDefinition = definition;
140     }
141 
142     /**
143      * Returns the currently visible window that is associated with the wallpaper in case we are
144      * transitioning from an activity with a wallpaper to one without.
145      */
getOldWallpaper()146     private @Nullable WindowState getOldWallpaper() {
147         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
148         final @TransitionType int firstTransit =
149                 mDisplayContent.mAppTransition.getFirstAppTransition();
150 
151         final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
152                 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */);
153         final boolean showWallpaper = wallpaperTarget != null
154                 && (wallpaperTarget.hasWallpaper()
155                 // Update task open transition to wallpaper transition when wallpaper is visible.
156                 // (i.e.launching app info activity from recent tasks)
157                 || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT)
158                 && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null)
159                 && mWallpaperControllerLocked.isWallpaperVisible()));
160         // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
161         // don't consider upgrading to wallpaper transition.
162         return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
163                 ? null : wallpaperTarget;
164     }
165 
166     /**
167      * Handle application transition for given display.
168      */
handleAppTransitionReady()169     void handleAppTransitionReady() {
170         mTempTransitionReasons.clear();
171         if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
172                 || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
173                 || !transitionGoodToGoForTaskFragments()) {
174             return;
175         }
176         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
177 
178         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
179         // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.
180         mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
181                 true /* traverseTopToBottom */);
182         // TODO(new-app-transition): Remove code using appTransition.getAppTransition()
183         final AppTransition appTransition = mDisplayContent.mAppTransition;
184 
185         mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
186 
187         appTransition.removeAppTransitionTimeoutCallbacks();
188 
189         mDisplayContent.mWallpaperMayChange = false;
190 
191         int appCount = mDisplayContent.mOpeningApps.size();
192         for (int i = 0; i < appCount; ++i) {
193             // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
194             // window is removed, or window relayout to invisible. This also affects window
195             // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
196             // transition selection depends on wallpaper target visibility.
197             mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
198         }
199         appCount = mDisplayContent.mChangingContainers.size();
200         for (int i = 0; i < appCount; ++i) {
201             // Clearing for same reason as above.
202             final ActivityRecord activity = getAppFromContainer(
203                     mDisplayContent.mChangingContainers.valueAtUnchecked(i));
204             if (activity != null) {
205                 activity.clearAnimatingFlags();
206             }
207         }
208 
209         // Adjust wallpaper before we pull the lower/upper target, since pending changes
210         // (like the clearAnimatingFlags() above) might affect wallpaper target result.
211         // Or, the opening app window should be a wallpaper target.
212         mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
213                 mDisplayContent.mOpeningApps);
214 
215         final @TransitionOldType int transit = getTransitCompatType(
216                 mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
217                 mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
218                 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
219                 mDisplayContent.mSkipAppTransitionAnimation);
220         mDisplayContent.mSkipAppTransitionAnimation = false;
221 
222         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
223                 "handleAppTransitionReady: displayId=%d appTransition={%s}"
224                 + " openingApps=[%s] closingApps=[%s] transit=%s",
225                 mDisplayContent.mDisplayId,
226                 appTransition.toString(),
227                 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
228                 AppTransition.appTransitionOldToString(transit));
229 
230         // Find the layout params of the top-most application window in the tokens, which is
231         // what will control the animation theme. If all closing windows are obscured, then there is
232         // no need to do an animation. This is the case, for example, when this transition is being
233         // done behind a dream window.
234         final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
235                 mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
236         final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
237         final ActivityRecord topOpeningApp =
238                 getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
239         final ActivityRecord topClosingApp =
240                 getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
241         final ActivityRecord topChangingApp =
242                 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
243         final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
244 
245         // Check if there is any override
246         if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
247             // Unfreeze the windows that were previously frozen for TaskFragment animation.
248             unfreezeEmbeddedChangingWindows();
249             overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
250         }
251 
252         final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
253                 || containsVoiceInteraction(mDisplayContent.mOpeningApps);
254 
255         final int layoutRedo;
256         mService.mSurfaceAnimationRunner.deferStartingAnimations();
257         try {
258             applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
259                     animLp, voiceInteraction);
260             handleClosingApps();
261             handleOpeningApps();
262             handleChangingApps(transit);
263 
264             appTransition.setLastAppTransition(transit, topOpeningApp,
265                     topClosingApp, topChangingApp);
266 
267             final int flags = appTransition.getTransitFlags();
268             layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
269             handleNonAppWindowsInTransition(transit, flags);
270             appTransition.postAnimationCallback();
271             appTransition.clear();
272         } finally {
273             mService.mSurfaceAnimationRunner.continueStartingAnimations();
274         }
275 
276         mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
277 
278         mDisplayContent.mOpeningApps.clear();
279         mDisplayContent.mClosingApps.clear();
280         mDisplayContent.mChangingContainers.clear();
281         mDisplayContent.mUnknownAppVisibilityController.clear();
282 
283         // This has changed the visibility of windows, so perform
284         // a new layout to get them all up-to-date.
285         mDisplayContent.setLayoutNeeded();
286 
287         mDisplayContent.computeImeTarget(true /* updateImeTarget */);
288 
289         mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
290                 mTempTransitionReasons);
291 
292         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
293 
294         mDisplayContent.pendingLayoutChanges |=
295                 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
296     }
297 
298     /**
299      * Get old transit type based on the current transit requests.
300      *
301      * @param appTransition {@link AppTransition} for managing app transition state.
302      * @param openingApps {@link ActivityRecord}s which are becoming visible.
303      * @param closingApps {@link ActivityRecord}s which are becoming invisible.
304      * @param changingContainers {@link WindowContainer}s which are changed in configuration.
305      * @param wallpaperTarget If non-null, this is the currently visible window that is associated
306      *                        with the wallpaper.
307      * @param oldWallpaper The currently visible window that is associated with the wallpaper in
308      *                     case we are transitioning from an activity with a wallpaper to one
309      *                     without. Otherwise null.
310      */
getTransitCompatType(AppTransition appTransition, ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation)311     static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
312             ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
313             ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
314             @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {
315 
316         // Determine if closing and opening app token sets are wallpaper targets, in which case
317         // special animations are needed.
318         final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
319                 && wallpaperTarget != null;
320         final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
321                 && wallpaperTarget != null;
322 
323         // Keyguard transit has highest priority.
324         switch (appTransition.getKeyguardTransition()) {
325             case TRANSIT_KEYGUARD_GOING_AWAY:
326                 return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
327                         : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
328             case TRANSIT_KEYGUARD_OCCLUDE:
329                 // When there is a closing app, the keyguard has already been occluded by an
330                 // activity, and another activity has started on top of that activity, so normal
331                 // app transition animation should be used.
332                 return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
333                         : TRANSIT_OLD_ACTIVITY_OPEN;
334             case TRANSIT_KEYGUARD_UNOCCLUDE:
335                 return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
336         }
337 
338         // This is not keyguard transition and one of the app has request to skip app transition.
339         if (skipAppTransitionAnimation) {
340             return WindowManager.TRANSIT_OLD_UNSET;
341         }
342         final @TransitionFlags int flags = appTransition.getTransitFlags();
343         final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
344 
345         // Special transitions
346         // TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
347         if (appTransition.containsTransitRequest(TRANSIT_CHANGE) && !changingContainers.isEmpty()) {
348             @TransitContainerType int changingType =
349                     getTransitContainerType(changingContainers.valueAt(0));
350             switch (changingType) {
351                 case TYPE_TASK:
352                     return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
353                 case TYPE_TASK_FRAGMENT:
354                     return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
355                 default:
356                     throw new IllegalStateException(
357                             "TRANSIT_CHANGE with unrecognized changing type=" + changingType);
358             }
359         }
360         if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
361             return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
362         }
363         if (firstTransit == TRANSIT_NONE) {
364             return TRANSIT_OLD_NONE;
365         }
366 
367         /*
368          * There are cases where we open/close a new task/activity, but in reality only a
369          * translucent activity on top of existing activities is opening/closing. For that one, we
370          * have a different animation because non of the task/activity animations actually work well
371          * with translucent apps.
372          */
373         if (isNormalTransit(firstTransit)) {
374             boolean allOpeningVisible = true;
375             boolean allTranslucentOpeningApps = !openingApps.isEmpty();
376             for (int i = openingApps.size() - 1; i >= 0; i--) {
377                 final ActivityRecord activity = openingApps.valueAt(i);
378                 if (!activity.isVisible()) {
379                     allOpeningVisible = false;
380                     if (activity.fillsParent()) {
381                         allTranslucentOpeningApps = false;
382                     }
383                 }
384             }
385             boolean allTranslucentClosingApps = !closingApps.isEmpty();
386             for (int i = closingApps.size() - 1; i >= 0; i--) {
387                 if (closingApps.valueAt(i).fillsParent()) {
388                     allTranslucentClosingApps = false;
389                     break;
390                 }
391             }
392 
393             if (allTranslucentClosingApps && allOpeningVisible) {
394                 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
395             }
396             if (allTranslucentOpeningApps && closingApps.isEmpty()) {
397                 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
398             }
399         }
400 
401         final ActivityRecord topOpeningApp = getTopApp(openingApps,
402                 false /* ignoreHidden */);
403         final ActivityRecord topClosingApp = getTopApp(closingApps,
404                 true /* ignoreHidden */);
405 
406         if (closingAppHasWallpaper && openingAppHasWallpaper) {
407             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
408             switch (firstTransit) {
409                 case TRANSIT_OPEN:
410                 case TRANSIT_TO_FRONT:
411                     return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
412                 case TRANSIT_CLOSE:
413                 case TRANSIT_TO_BACK:
414                     return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
415             }
416         } else if (oldWallpaper != null && !openingApps.isEmpty()
417                 && !openingApps.contains(oldWallpaper.mActivityRecord)
418                 && closingApps.contains(oldWallpaper.mActivityRecord)
419                 && topClosingApp == oldWallpaper.mActivityRecord) {
420             // We are transitioning from an activity with a wallpaper to one without.
421             return TRANSIT_OLD_WALLPAPER_CLOSE;
422         } else if (wallpaperTarget != null && wallpaperTarget.isVisible()
423                 && openingApps.contains(wallpaperTarget.mActivityRecord)
424                 && topOpeningApp == wallpaperTarget.mActivityRecord
425                 /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) {
426             // We are transitioning from an activity without
427             // a wallpaper to now showing the wallpaper
428             return TRANSIT_OLD_WALLPAPER_OPEN;
429         }
430 
431         final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
432                 openingApps, closingApps, true /* visible */);
433         final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
434                 openingApps, closingApps, false /* visible */);
435         final WindowContainer<?> openingContainer = !openingWcs.isEmpty()
436                 ? openingWcs.valueAt(0) : null;
437         final WindowContainer<?> closingContainer = !closingWcs.isEmpty()
438                 ? closingWcs.valueAt(0) : null;
439         @TransitContainerType int openingType = getTransitContainerType(openingContainer);
440         @TransitContainerType int closingType = getTransitContainerType(closingContainer);
441         if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) {
442             return TRANSIT_OLD_TASK_TO_FRONT;
443         }
444         if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) {
445             return TRANSIT_OLD_TASK_TO_BACK;
446         }
447         if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
448             if (openingType == TYPE_TASK) {
449                 return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
450                         ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
451             }
452             if (openingType == TYPE_ACTIVITY) {
453                 return TRANSIT_OLD_ACTIVITY_OPEN;
454             }
455             if (openingType == TYPE_TASK_FRAGMENT) {
456                 return TRANSIT_OLD_TASK_FRAGMENT_OPEN;
457             }
458         }
459         if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
460             if (closingType == TYPE_TASK) {
461                 return TRANSIT_OLD_TASK_CLOSE;
462             }
463             if (closingType == TYPE_TASK_FRAGMENT) {
464                 return TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
465             }
466             if (closingType == TYPE_ACTIVITY) {
467                 for (int i = closingApps.size() - 1; i >= 0; i--) {
468                     if (closingApps.valueAt(i).visibleIgnoringKeyguard) {
469                         return TRANSIT_OLD_ACTIVITY_CLOSE;
470                     }
471                 }
472                 // Skip close activity transition since no closing app can be visible
473                 return WindowManager.TRANSIT_OLD_UNSET;
474             }
475         }
476         if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH)
477                 && !openingWcs.isEmpty() && !openingApps.isEmpty()) {
478             return TRANSIT_OLD_ACTIVITY_RELAUNCH;
479         }
480         return TRANSIT_OLD_NONE;
481     }
482 
483     @TransitContainerType
getTransitContainerType(@ullable WindowContainer<?> container)484     private static int getTransitContainerType(@Nullable WindowContainer<?> container) {
485         if (container == null) {
486             return TYPE_NONE;
487         }
488         if (container.asTask() != null) {
489             return TYPE_TASK;
490         }
491         if (container.asTaskFragment() != null) {
492             return TYPE_TASK_FRAGMENT;
493         }
494         if (container.asActivityRecord() != null) {
495             return TYPE_ACTIVITY;
496         }
497         return TYPE_NONE;
498     }
499 
500     @Nullable
getAnimLp(ActivityRecord activity)501     private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
502         final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
503         return mainWindow != null ? mainWindow.mAttrs : null;
504     }
505 
getRemoteAnimationOverride(@ullable WindowContainer container, @TransitionOldType int transit, ArraySet<Integer> activityTypes)506     RemoteAnimationAdapter getRemoteAnimationOverride(@Nullable WindowContainer container,
507             @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
508         if (container != null) {
509             final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
510             if (definition != null) {
511                 final RemoteAnimationAdapter adapter = definition.getAdapter(transit,
512                         activityTypes);
513                 if (adapter != null) {
514                     return adapter;
515                 }
516             }
517         }
518         return mRemoteAnimationDefinition != null
519                 ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes)
520                 : null;
521     }
522 
unfreezeEmbeddedChangingWindows()523     private void unfreezeEmbeddedChangingWindows() {
524         final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers;
525         for (int i = changingContainers.size() - 1; i >= 0; i--) {
526             final WindowContainer wc = changingContainers.valueAt(i);
527             if (wc.isEmbedded()) {
528                 wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction());
529             }
530         }
531     }
532 
transitionMayContainNonAppWindows(@ransitionOldType int transit)533     private boolean transitionMayContainNonAppWindows(@TransitionOldType int transit) {
534         // We don't want to have the client to animate any non-app windows.
535         // Having {@code transit} of those types doesn't mean it will contain non-app windows, but
536         // non-app windows will only be included with those transition types. And we don't currently
537         // have any use case of those for TaskFragment transition.
538         return shouldStartNonAppWindowAnimationsForKeyguardExit(transit)
539                 || shouldAttachNavBarToApp(mService, mDisplayContent, transit)
540                 || shouldStartWallpaperAnimation(mDisplayContent);
541     }
542 
543     /**
544      * Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all app windows
545      * in the current transition.
546      * @return {@code null} if there is no such organizer, or if there are more than one.
547      */
548     @Nullable
findTaskFragmentOrganizerForAllWindows()549     private ITaskFragmentOrganizer findTaskFragmentOrganizerForAllWindows() {
550         mTempTransitionWindows.clear();
551         mTempTransitionWindows.addAll(mDisplayContent.mClosingApps);
552         mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps);
553         mTempTransitionWindows.addAll(mDisplayContent.mChangingContainers);
554 
555         // It should only animated by the organizer if all windows are below the same leaf Task.
556         Task leafTask = null;
557         for (int i = mTempTransitionWindows.size() - 1; i >= 0; i--) {
558             final ActivityRecord r = getAppFromContainer(mTempTransitionWindows.get(i));
559             if (r == null) {
560                 leafTask = null;
561                 break;
562             }
563             // The activity may be a child of embedded Task, but we want to find the owner Task.
564             // As a result, find the organized TaskFragment first.
565             final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
566             // There are also cases where the Task contains non-embedded activity, such as launching
567             // split TaskFragments from a non-embedded activity.
568             // The hierarchy may looks like this:
569             // - Task
570             //    - Activity
571             //    - TaskFragment
572             //       - Activity
573             //    - TaskFragment
574             //       - Activity
575             // We also want to have the organizer handle the transition for such case.
576             final Task task = organizedTaskFragment != null
577                     ? organizedTaskFragment.getTask()
578                     : r.getTask();
579             if (task == null) {
580                 leafTask = null;
581                 break;
582             }
583             // We don't want the organizer to handle transition of other non-embedded Task.
584             if (leafTask != null && leafTask != task) {
585                 leafTask = null;
586                 break;
587             }
588             final ActivityRecord rootActivity = task.getRootActivity();
589             // We don't want the organizer to handle transition when the whole app is closing.
590             if (rootActivity == null) {
591                 leafTask = null;
592                 break;
593             }
594             // We don't want the organizer to handle transition of non-embedded activity of other
595             // app.
596             if (r.getUid() != task.effectiveUid && !r.isEmbedded()) {
597                 leafTask = null;
598                 break;
599             }
600             leafTask = task;
601         }
602         mTempTransitionWindows.clear();
603         if (leafTask == null) {
604             return null;
605         }
606 
607         // We don't support remote animation for Task with multiple TaskFragmentOrganizers.
608         final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1];
609         final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> {
610             final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer();
611             if (tfOrganizer == null) {
612                 return false;
613             }
614             if (organizer[0] != null && !organizer[0].asBinder().equals(tfOrganizer.asBinder())) {
615                 return true;
616             }
617             organizer[0] = tfOrganizer;
618             return false;
619         });
620         if (hasMultipleOrganizers) {
621             ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for"
622                     + " Task with multiple TaskFragmentOrganizers.");
623             return null;
624         }
625         return organizer[0];
626     }
627 
628     /**
629      * Overrides the pending transition with the remote animation defined by the
630      * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
631      * {@link TaskFragment} that are organized by the same organizer.
632      *
633      * @return {@code true} if the transition is overridden.
634      */
overrideWithTaskFragmentRemoteAnimation(@ransitionOldType int transit, ArraySet<Integer> activityTypes)635     private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
636             ArraySet<Integer> activityTypes) {
637         if (transitionMayContainNonAppWindows(transit)) {
638             return false;
639         }
640 
641         final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizerForAllWindows();
642         final RemoteAnimationDefinition definition = organizer != null
643                 ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
644                     .getRemoteAnimationDefinition(organizer)
645                 : null;
646         final RemoteAnimationAdapter adapter = definition != null
647                 ? definition.getAdapter(transit, activityTypes)
648                 : null;
649         if (adapter == null) {
650             return false;
651         }
652         mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
653         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
654                 "Override with TaskFragment remote animation for transit=%s",
655                 AppTransition.appTransitionOldToString(transit));
656         return true;
657     }
658 
659     /**
660      * Overrides the pending transition with the remote animation defined for the transition in the
661      * set of defined remote animations in the app window token.
662      */
overrideWithRemoteAnimationIfSet(@ullable ActivityRecord animLpActivity, @TransitionOldType int transit, ArraySet<Integer> activityTypes)663     private void overrideWithRemoteAnimationIfSet(@Nullable ActivityRecord animLpActivity,
664             @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
665         if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
666             // The crash transition has higher priority than any involved remote animations.
667             return;
668         }
669         final RemoteAnimationAdapter adapter =
670                 getRemoteAnimationOverride(animLpActivity, transit, activityTypes);
671         if (adapter != null
672                 && mDisplayContent.mAppTransition.getRemoteAnimationController() == null) {
673             mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
674         }
675     }
676 
677     @Nullable
findRootTaskFromContainer(WindowContainer wc)678     static Task findRootTaskFromContainer(WindowContainer wc) {
679         return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask()
680                 : wc.asActivityRecord().getRootTask();
681     }
682 
683     @Nullable
getAppFromContainer(WindowContainer wc)684     static ActivityRecord getAppFromContainer(WindowContainer wc) {
685         return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
686                 : wc.asActivityRecord();
687     }
688 
689     /**
690      * @return The window token that determines the animation theme.
691      */
692     @Nullable
findAnimLayoutParamsToken(@ransitionOldType int transit, ArraySet<Integer> activityTypes)693     private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
694             ArraySet<Integer> activityTypes) {
695         ActivityRecord result;
696         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
697         final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
698         final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
699 
700         // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
701         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
702                 w -> w.getRemoteAnimationDefinition() != null
703                         && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
704         if (result != null) {
705             return result;
706         }
707         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
708                 w -> w.fillsParent() && w.findMainWindow() != null);
709         if (result != null) {
710             return result;
711         }
712         return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
713                 w -> w.findMainWindow() != null);
714     }
715 
716     /**
717      * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
718      *         of apps in {@code array1}, {@code array2}, and {@code array3}.
719      */
collectActivityTypes(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3)720     private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
721             ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
722         final ArraySet<Integer> result = new ArraySet<>();
723         for (int i = array1.size() - 1; i >= 0; i--) {
724             result.add(array1.valueAt(i).getActivityType());
725         }
726         for (int i = array2.size() - 1; i >= 0; i--) {
727             result.add(array2.valueAt(i).getActivityType());
728         }
729         for (int i = array3.size() - 1; i >= 0; i--) {
730             result.add(array3.valueAt(i).getActivityType());
731         }
732         return result;
733     }
734 
lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, Predicate<ActivityRecord> filter)735     private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
736             ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3,
737             Predicate<ActivityRecord> filter) {
738         final int array2base = array1.size();
739         final int array3base = array2.size() + array2base;
740         final int count = array3base + array3.size();
741         int bestPrefixOrderIndex = Integer.MIN_VALUE;
742         ActivityRecord bestToken = null;
743         for (int i = 0; i < count; i++) {
744             final WindowContainer wtoken = i < array2base
745                     ? array1.valueAt(i)
746                     : (i < array3base
747                             ? array2.valueAt(i - array2base)
748                             : array3.valueAt(i - array3base));
749             final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
750             final ActivityRecord r = getAppFromContainer(wtoken);
751             if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) {
752                 bestPrefixOrderIndex = prefixOrderIndex;
753                 bestToken = r;
754             }
755         }
756         return bestToken;
757     }
758 
759     private boolean containsVoiceInteraction(ArraySet<ActivityRecord> apps) {
760         for (int i = apps.size() - 1; i >= 0; i--) {
761             if (apps.valueAt(i).mVoiceInteraction) {
762                 return true;
763             }
764         }
765         return false;
766     }
767 
768     /**
769      * Apply animation to the set of window containers.
770      *
771      * @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
772      * @param apps The list of {@link ActivityRecord}s being transitioning.
773      * @param transit The current transition type.
774      * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
775      *                invisible.
776      * @param animLp Layout parameters in which an app transition animation runs.
777      * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
778      *                         interaction session driving task.
779      */
applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps, @TransitionOldType int transit, boolean visible, LayoutParams animLp, boolean voiceInteraction)780     private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
781             @TransitionOldType int transit, boolean visible, LayoutParams animLp,
782             boolean voiceInteraction) {
783         final int wcsCount = wcs.size();
784         for (int i = 0; i < wcsCount; i++) {
785             final WindowContainer wc = wcs.valueAt(i);
786             // If app transition animation target is promoted to higher level, SurfaceAnimator
787             // triggers WC#onAnimationFinished only on the promoted target. So we need to take care
788             // of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
789             // app transition.
790             final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
791             for (int j = 0; j < apps.size(); ++j) {
792                 final ActivityRecord app = apps.valueAt(j);
793                 if (app.isDescendantOf(wc)) {
794                     transitioningDescendants.add(app);
795                 }
796             }
797             wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
798         }
799     }
800 
801     /**
802      * Find WindowContainers to be animated from a set of opening and closing apps. We will promote
803      * animation targets to higher level in the window hierarchy if possible.
804      *
805      * @param visible {@code true} to get animation targets for opening apps, {@code false} to get
806      *                            animation targets for closing apps.
807      * @return {@link WindowContainer}s to be animated.
808      */
809     @VisibleForTesting
getAnimationTargets( ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, boolean visible)810     static ArraySet<WindowContainer> getAnimationTargets(
811             ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
812             boolean visible) {
813 
814         // The candidates of animation targets, which might be able to promote to higher level.
815         final LinkedList<WindowContainer> candidates = new LinkedList<>();
816         final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps;
817         for (int i = 0; i < apps.size(); ++i) {
818             final ActivityRecord app = apps.valueAt(i);
819             if (app.shouldApplyAnimation(visible)) {
820                 candidates.add(app);
821                 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
822                         "Changing app %s visible=%b performLayout=%b",
823                         app, app.isVisible(), false);
824             }
825         }
826 
827         final ArraySet<ActivityRecord> otherApps = visible ? closingApps : openingApps;
828         // Ancestors of closing apps while finding animation targets for opening apps, or ancestors
829         // of opening apps while finding animation targets for closing apps.
830         final ArraySet<WindowContainer> otherAncestors = new ArraySet<>();
831         for (int i = 0; i < otherApps.size(); ++i) {
832             for (WindowContainer wc = otherApps.valueAt(i); wc != null; wc = wc.getParent()) {
833                 otherAncestors.add(wc);
834             }
835         }
836 
837         // The final animation targets which cannot promote to higher level anymore.
838         final ArraySet<WindowContainer> targets = new ArraySet<>();
839         final ArrayList<WindowContainer> siblings = new ArrayList<>();
840         while (!candidates.isEmpty()) {
841             final WindowContainer current = candidates.removeFirst();
842             final WindowContainer parent = current.getParent();
843             siblings.clear();
844             siblings.add(current);
845             boolean canPromote = true;
846 
847             if (parent == null || !parent.canCreateRemoteAnimationTarget()
848                     || !parent.canBeAnimationTarget()
849                     // We cannot promote the animation on Task's parent when the task is in
850                     // clearing task in case the animating get stuck when performing the opening
851                     // task that behind it.
852                     || (current.asTask() != null && current.asTask().mInRemoveTask)) {
853                 canPromote = false;
854             } else {
855                 // In case a descendant of the parent belongs to the other group, we cannot promote
856                 // the animation target from "current" to the parent.
857                 //
858                 // Example: Imagine we're checking if we can animate a Task instead of a set of
859                 // ActivityRecords. In case an activity starts a new activity within a same Task,
860                 // an ActivityRecord of an existing activity belongs to the opening apps, at the
861                 // same time, the other ActivityRecord of a new activity belongs to the closing
862                 // apps. In this case, we cannot promote the animation target to Task level, but
863                 // need to animate each individual activity.
864                 //
865                 // [Task] +- [ActivityRecord1] (in opening apps)
866                 //        +- [ActivityRecord2] (in closing apps)
867                 if (otherAncestors.contains(parent)) {
868                     canPromote = false;
869                 }
870 
871                 // Find all siblings of the current WindowContainer in "candidates", move them into
872                 // a separate list "siblings", and checks if an animation target can be promoted
873                 // to its parent.
874                 //
875                 // We can promote an animation target to its parent if and only if all visible
876                 // siblings will be animating.
877                 //
878                 // Example: Imagine that a Task contains two visible activity record, but only one
879                 // of them is included in the opening apps and the other belongs to neither opening
880                 // or closing apps. This happens when an activity launches another translucent
881                 // activity in the same Task. In this case, we cannot animate Task, but have to
882                 // animate each activity, otherwise an activity behind the translucent activity also
883                 // animates.
884                 //
885                 // [Task] +- [ActivityRecord1] (visible, in opening apps)
886                 //        +- [ActivityRecord2] (visible, not in opening apps)
887                 for (int j = 0; j < parent.getChildCount(); ++j) {
888                     final WindowContainer sibling = parent.getChildAt(j);
889                     if (candidates.remove(sibling)) {
890                         siblings.add(sibling);
891                     } else if (sibling != current && sibling.isVisible()) {
892                         canPromote = false;
893                     }
894                 }
895             }
896 
897             if (canPromote) {
898                 candidates.add(parent);
899             } else {
900                 targets.addAll(siblings);
901             }
902         }
903         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "getAnimationTarget in=%s, out=%s",
904                 apps, targets);
905         return targets;
906     }
907 
908     /**
909      * Apply an app transition animation based on a set of {@link ActivityRecord}
910      *
911      * @param openingApps The list of opening apps to which an app transition animation applies.
912      * @param closingApps The list of closing apps to which an app transition animation applies.
913      * @param transit The current transition type.
914      * @param animLp Layout parameters in which an app transition animation runs.
915      * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
916      *                         interaction session driving task.
917      */
applyAnimations(ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit, LayoutParams animLp, boolean voiceInteraction)918     private void applyAnimations(ArraySet<ActivityRecord> openingApps,
919             ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
920             LayoutParams animLp, boolean voiceInteraction) {
921         if (transit == WindowManager.TRANSIT_OLD_UNSET
922                 || (openingApps.isEmpty() && closingApps.isEmpty())) {
923             return;
924         }
925 
926         final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
927                 openingApps, closingApps, true /* visible */);
928         final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
929                 openingApps, closingApps, false /* visible */);
930         applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
931                 voiceInteraction);
932         applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
933                 voiceInteraction);
934         final RecentsAnimationController rac = mService.getRecentsAnimationController();
935         if (rac != null) {
936             rac.sendTasksAppeared();
937         }
938 
939         for (int i = 0; i < openingApps.size(); ++i) {
940             openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
941         }
942         for (int i = 0; i < closingApps.size(); ++i) {
943             closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
944         }
945 
946         final AccessibilityController accessibilityController =
947                 mDisplayContent.mWmService.mAccessibilityController;
948         if (accessibilityController.hasCallbacks()) {
949             accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
950         }
951     }
952 
handleOpeningApps()953     private void handleOpeningApps() {
954         final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
955         final int appsCount = openingApps.size();
956 
957         for (int i = 0; i < appsCount; i++) {
958             final ActivityRecord app = openingApps.valueAt(i);
959             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
960 
961             app.commitVisibility(true /* visible */, false /* performLayout */);
962 
963             // In case a trampoline activity is used, it can happen that a new ActivityRecord is
964             // added and a new app transition starts before the previous app transition animation
965             // ends. So we cannot simply use app.isAnimating(PARENTS) to determine if the app must
966             // to be added to the list of tokens to be notified of app transition complete.
967             final WindowContainer wc = app.getAnimatingContainer(PARENTS,
968                     ANIMATION_TYPE_APP_TRANSITION);
969             if (wc == null || !wc.getAnimationSources().contains(app)) {
970                 // This token isn't going to be animating. Add it to the list of tokens to
971                 // be notified of app transition complete since the notification will not be
972                 // sent be the app window animator.
973                 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token);
974             }
975             app.updateReportedVisibilityLocked();
976             app.waitingToShow = false;
977             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
978                     ">>> OPEN TRANSACTION handleAppTransitionReady()");
979             mService.openSurfaceTransaction();
980             try {
981                 app.showAllWindowsLocked();
982             } finally {
983                 mService.closeSurfaceTransaction("handleAppTransitionReady");
984                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
985                         "<<< CLOSE TRANSACTION handleAppTransitionReady()");
986             }
987 
988             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
989                 app.attachThumbnailAnimation();
990             } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
991                 app.attachCrossProfileAppsThumbnailAnimation();
992             }
993         }
994     }
995 
handleClosingApps()996     private void handleClosingApps() {
997         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
998         final int appsCount = closingApps.size();
999 
1000         for (int i = 0; i < appsCount; i++) {
1001             final ActivityRecord app = closingApps.valueAt(i);
1002             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
1003 
1004             app.commitVisibility(false /* visible */, false /* performLayout */);
1005             app.updateReportedVisibilityLocked();
1006             // Force the allDrawn flag, because we want to start
1007             // this guy's animations regardless of whether it's
1008             // gotten drawn.
1009             app.allDrawn = true;
1010             // Ensure that apps that are mid-starting are also scheduled to have their
1011             // starting windows removed after the animation is complete
1012             if (app.mStartingWindow != null && !app.mStartingWindow.mAnimatingExit) {
1013                 app.removeStartingWindow();
1014             }
1015 
1016             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
1017                 app.attachThumbnailAnimation();
1018             }
1019         }
1020     }
1021 
handleChangingApps(@ransitionOldType int transit)1022     private void handleChangingApps(@TransitionOldType int transit) {
1023         final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
1024         final int appsCount = apps.size();
1025         for (int i = 0; i < appsCount; i++) {
1026             WindowContainer wc = apps.valueAt(i);
1027             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
1028             wc.applyAnimation(null, transit, true, false, null /* sources */);
1029         }
1030     }
1031 
handleNonAppWindowsInTransition(@ransitionOldType int transit, int flags)1032     private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) {
1033         if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
1034                 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
1035             if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
1036                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
1037                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
1038                 Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
1039                         (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
1040                 if (anim != null) {
1041                     anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
1042                     mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
1043                 }
1044             }
1045         }
1046         if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
1047                 || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
1048                 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
1049             mDisplayContent.startKeyguardExitOnNonAppWindows(
1050                     transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
1051                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
1052                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
1053         }
1054     }
1055 
transitionGoodToGo(ArraySet<? extends WindowContainer> apps, ArrayMap<WindowContainer, Integer> outReasons)1056     private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
1057             ArrayMap<WindowContainer, Integer> outReasons) {
1058         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
1059                 "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
1060                 mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
1061         if (mDisplayContent.mAppTransition.isTimeout()) {
1062             return true;
1063         }
1064         final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
1065                 Display.DEFAULT_DISPLAY).getRotationAnimation();
1066 
1067         // Imagine the case where we are changing orientation due to an app transition, but a
1068         // previous orientation change is still in progress. We won't process the orientation
1069         // change for our transition because we need to wait for the rotation animation to
1070         // finish.
1071         // If we start the app transition at this point, we will interrupt it halfway with a
1072         // new rotation animation after the old one finally finishes. It's better to defer the
1073         // app transition.
1074         if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
1075                 && mDisplayContent.getDisplayRotation().needsUpdate()) {
1076             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
1077                     "Delaying app transition for screen rotation animation to finish");
1078             return false;
1079         }
1080         for (int i = 0; i < apps.size(); i++) {
1081             WindowContainer wc = apps.valueAt(i);
1082             final ActivityRecord activity = getAppFromContainer(wc);
1083             if (activity == null) {
1084                 continue;
1085             }
1086             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
1087                     "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
1088                             + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
1089                     activity, activity.allDrawn, activity.startingDisplayed,
1090                     activity.startingMoved, activity.isRelaunching(),
1091                     activity.mStartingWindow);
1092 
1093             final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
1094             if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
1095                 return false;
1096             }
1097             if (allDrawn) {
1098                 outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
1099             } else {
1100                 outReasons.put(activity,
1101                         activity.mStartingData instanceof SplashScreenStartingData
1102                                 ? APP_TRANSITION_SPLASH_SCREEN
1103                                 : APP_TRANSITION_SNAPSHOT);
1104             }
1105         }
1106 
1107         // We also need to wait for the specs to be fetched, if needed.
1108         if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
1109             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
1110             return false;
1111         }
1112 
1113         if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
1114             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
1115                     mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
1116             return false;
1117         }
1118 
1119         // If the wallpaper is visible, we need to check it's ready too.
1120         return !mWallpaperControllerLocked.isWallpaperVisible()
1121                 || mWallpaperControllerLocked.wallpaperTransitionReady();
1122     }
1123 
transitionGoodToGoForTaskFragments()1124     private boolean transitionGoodToGoForTaskFragments() {
1125         if (mDisplayContent.mAppTransition.isTimeout()) {
1126             return true;
1127         }
1128 
1129         // Check all Tasks in this transition. This is needed because new TaskFragment created for
1130         // launching activity may not be in the tracking lists, but we still want to wait for the
1131         // activity launch to start the transition.
1132         final ArraySet<Task> rootTasks = new ArraySet<>();
1133         for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
1134             rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask());
1135         }
1136         for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
1137             rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask());
1138         }
1139         for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
1140             rootTasks.add(
1141                     findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i)));
1142         }
1143 
1144         // Organized TaskFragment can be empty for two situations:
1145         // 1. New created and is waiting for Activity launch. In this case, we want to wait for
1146         //    the Activity launch to trigger the transition.
1147         // 2. Last Activity is just removed. In this case, we want to wait for organizer to
1148         //    remove the TaskFragment because it may also want to change other TaskFragments in
1149         //    the same transition.
1150         for (int i = rootTasks.size() - 1; i >= 0; i--) {
1151             final Task rootTask = rootTasks.valueAt(i);
1152             if (rootTask == null) {
1153                 // It is possible that one activity may have been removed from the hierarchy. No
1154                 // need to check for this case.
1155                 continue;
1156             }
1157             final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
1158                 if (!taskFragment.isReadyToTransit()) {
1159                     ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
1160                             taskFragment);
1161                     return true;
1162                 }
1163                 return false;
1164             });
1165             if (notReady) {
1166                 return false;
1167             }
1168         }
1169         return true;
1170     }
1171 
1172     /**
1173      * Identifies whether the current transition occurs within a single task or not. This is used
1174      * to determine whether animations should be clipped to the task bounds instead of root task
1175      * bounds.
1176      */
1177     @VisibleForTesting
isTransitWithinTask(@ransitionOldType int transit, Task task)1178     boolean isTransitWithinTask(@TransitionOldType int transit, Task task) {
1179         if (task == null
1180                 || !mDisplayContent.mChangingContainers.isEmpty()) {
1181             // if there is no task, then we can't constrain to the task.
1182             // if anything is changing, it can animate outside its task.
1183             return false;
1184         }
1185         if (!(transit == TRANSIT_OLD_ACTIVITY_OPEN
1186                 || transit == TRANSIT_OLD_ACTIVITY_CLOSE
1187                 || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH)) {
1188             // only activity-level transitions will be within-task.
1189             return false;
1190         }
1191         // check that all components are in the task.
1192         for (ActivityRecord activity : mDisplayContent.mOpeningApps) {
1193             Task activityTask = activity.getTask();
1194             if (activityTask != task) {
1195                 return false;
1196             }
1197         }
1198         for (ActivityRecord activity : mDisplayContent.mClosingApps) {
1199             if (activity.getTask() != task) {
1200                 return false;
1201             }
1202         }
1203         return true;
1204     }
1205 
canBeWallpaperTarget(ArraySet<ActivityRecord> apps)1206     private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
1207         for (int i = apps.size() - 1; i >= 0; i--) {
1208             if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
1209                 return true;
1210             }
1211         }
1212         return false;
1213     }
1214 
1215     /**
1216      * Finds the top app in a list of apps, using its {@link ActivityRecord#getPrefixOrderIndex} to
1217      * compare z-order.
1218      *
1219      * @param apps The list of apps to search.
1220      * @param ignoreInvisible If set to true, ignores apps that are not
1221      *                        {@link ActivityRecord#isVisible}.
1222      * @return The top {@link ActivityRecord}.
1223      */
getTopApp(ArraySet<? extends WindowContainer> apps, boolean ignoreInvisible)1224     private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
1225             boolean ignoreInvisible) {
1226         int topPrefixOrderIndex = Integer.MIN_VALUE;
1227         ActivityRecord topApp = null;
1228         for (int i = apps.size() - 1; i >= 0; i--) {
1229             final ActivityRecord app = getAppFromContainer(apps.valueAt(i));
1230             if (app == null || ignoreInvisible && !app.isVisible()) {
1231                 continue;
1232             }
1233             final int prefixOrderIndex = app.getPrefixOrderIndex();
1234             if (prefixOrderIndex > topPrefixOrderIndex) {
1235                 topPrefixOrderIndex = prefixOrderIndex;
1236                 topApp = app;
1237             }
1238         }
1239         return topApp;
1240     }
1241 }
1242