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