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