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