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