• 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.LayoutParams.FLAG_SHOW_WALLPAPER;
20 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
21 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
22 import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
23 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
24 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
25 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
26 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
27 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
28 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
29 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
30 import static android.view.WindowManager.TRANSIT_NONE;
31 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
32 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
33 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
34 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
35 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
36 import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
37 import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
38 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
39 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
40 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
41 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
42 
43 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
44 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
45 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
46 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
47 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
48 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
49 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
50 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
51 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
52 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
53 
54 import android.os.SystemClock;
55 import android.os.Trace;
56 import android.util.ArraySet;
57 import android.util.Slog;
58 import android.util.SparseIntArray;
59 import android.view.Display;
60 import android.view.RemoteAnimationAdapter;
61 import android.view.RemoteAnimationDefinition;
62 import android.view.WindowManager;
63 import android.view.WindowManager.LayoutParams;
64 import android.view.animation.Animation;
65 
66 import com.android.internal.annotations.VisibleForTesting;
67 
68 import java.util.function.Predicate;
69 
70 
71 /**
72  * Checks for app transition readiness, resolves animation attributes and performs visibility
73  * change for apps that animate as part of an app transition.
74  */
75 public class AppTransitionController {
76     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM;
77     private final WindowManagerService mService;
78     private final DisplayContent mDisplayContent;
79     private final WallpaperController mWallpaperControllerLocked;
80     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
81 
82     private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
83 
AppTransitionController(WindowManagerService service, DisplayContent displayContent)84     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
85         mService = service;
86         mDisplayContent = displayContent;
87         mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
88     }
89 
registerRemoteAnimations(RemoteAnimationDefinition definition)90     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
91         mRemoteAnimationDefinition = definition;
92     }
93 
94     /**
95      * Handle application transition for given display.
96      */
handleAppTransitionReady()97     void handleAppTransitionReady() {
98         mTempTransitionReasons.clear();
99         if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
100                 || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
101             return;
102         }
103         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
104 
105         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
106         final AppTransition appTransition = mDisplayContent.mAppTransition;
107         int transit = appTransition.getAppTransition();
108         if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
109             transit = WindowManager.TRANSIT_UNSET;
110         }
111         mDisplayContent.mSkipAppTransitionAnimation = false;
112         mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
113 
114         appTransition.removeAppTransitionTimeoutCallbacks();
115 
116         mDisplayContent.mWallpaperMayChange = false;
117 
118         int appCount = mDisplayContent.mOpeningApps.size();
119         for (int i = 0; i < appCount; ++i) {
120             // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
121             // window is removed, or window relayout to invisible. This also affects window
122             // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
123             // transition selection depends on wallpaper target visibility.
124             mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
125         }
126         appCount = mDisplayContent.mChangingApps.size();
127         for (int i = 0; i < appCount; ++i) {
128             // Clearing for same reason as above.
129             mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
130         }
131 
132         // Adjust wallpaper before we pull the lower/upper target, since pending changes
133         // (like the clearAnimatingFlags() above) might affect wallpaper target result.
134         // Or, the opening app window should be a wallpaper target.
135         mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
136                 mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
137 
138         // Determine if closing and opening app token sets are wallpaper targets, in which case
139         // special animations are needed.
140         final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
141         final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
142                 && hasWallpaperTarget;
143         final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
144                 && hasWallpaperTarget;
145 
146         transit = maybeUpdateTransitToTranslucentAnim(transit);
147         transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
148                 closingAppHasWallpaper);
149 
150         // Find the layout params of the top-most application window in the tokens, which is
151         // what will control the animation theme. If all closing windows are obscured, then there is
152         // no need to do an animation. This is the case, for example, when this transition is being
153         // done behind a dream window.
154         final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
155                 mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
156         final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
157         final AppWindowToken animLpToken = allowAnimations
158                 ? findAnimLayoutParamsToken(transit, activityTypes)
159                 : null;
160         final AppWindowToken topOpeningApp = allowAnimations
161                 ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */)
162                 : null;
163         final AppWindowToken topClosingApp = allowAnimations
164                 ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
165                 : null;
166         final AppWindowToken topChangingApp = allowAnimations
167                 ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
168                 : null;
169         final WindowManager.LayoutParams animLp = getAnimLp(animLpToken);
170         overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
171 
172         final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
173                 || containsVoiceInteraction(mDisplayContent.mOpeningApps)
174                 || containsVoiceInteraction(mDisplayContent.mChangingApps);
175 
176         final int layoutRedo;
177         mService.mSurfaceAnimationRunner.deferStartingAnimations();
178         try {
179             processApplicationsAnimatingInPlace(transit);
180 
181             handleClosingApps(transit, animLp, voiceInteraction);
182             handleOpeningApps(transit, animLp, voiceInteraction);
183             handleChangingApps(transit, animLp, voiceInteraction);
184 
185             appTransition.setLastAppTransition(transit, topOpeningApp,
186                     topClosingApp, topChangingApp);
187 
188             final int flags = appTransition.getTransitFlags();
189             layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
190                     mDisplayContent.mOpeningApps);
191             handleNonAppWindowsInTransition(transit, flags);
192             appTransition.postAnimationCallback();
193             appTransition.clear();
194         } finally {
195             mService.mSurfaceAnimationRunner.continueStartingAnimations();
196         }
197 
198         mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
199 
200         mDisplayContent.mOpeningApps.clear();
201         mDisplayContent.mClosingApps.clear();
202         mDisplayContent.mChangingApps.clear();
203         mDisplayContent.mUnknownAppVisibilityController.clear();
204 
205         // This has changed the visibility of windows, so perform
206         // a new layout to get them all up-to-date.
207         mDisplayContent.setLayoutNeeded();
208 
209         mDisplayContent.computeImeTarget(true /* updateImeTarget */);
210 
211         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
212                 SystemClock.uptimeMillis());
213 
214         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
215 
216         mDisplayContent.pendingLayoutChanges |=
217                 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
218     }
219 
getAnimLp(AppWindowToken wtoken)220     private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) {
221         final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
222         return mainWindow != null ? mainWindow.mAttrs : null;
223     }
224 
getRemoteAnimationOverride(AppWindowToken animLpToken, int transit, ArraySet<Integer> activityTypes)225     RemoteAnimationAdapter getRemoteAnimationOverride(AppWindowToken animLpToken, int transit,
226             ArraySet<Integer> activityTypes) {
227         final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
228         if (definition != null) {
229             final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
230             if (adapter != null) {
231                 return adapter;
232             }
233         }
234         if (mRemoteAnimationDefinition == null) {
235             return null;
236         }
237         return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
238     }
239 
240     /**
241      * Overrides the pending transition with the remote animation defined for the transition in the
242      * set of defined remote animations in the app window token.
243      */
overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, ArraySet<Integer> activityTypes)244     private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
245             ArraySet<Integer> activityTypes) {
246         if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
247             // The crash transition has higher priority than any involved remote animations.
248             return;
249         }
250         if (animLpToken == null) {
251             return;
252         }
253         final RemoteAnimationAdapter adapter =
254                 getRemoteAnimationOverride(animLpToken, transit, activityTypes);
255         if (adapter != null) {
256             animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
257                     adapter);
258         }
259     }
260 
261     /**
262      * @return The window token that determines the animation theme.
263      */
findAnimLayoutParamsToken(@indowManager.TransitionType int transit, ArraySet<Integer> activityTypes)264     private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
265             ArraySet<Integer> activityTypes) {
266         AppWindowToken result;
267         final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
268         final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
269         final ArraySet<AppWindowToken> changingApps = mDisplayContent.mChangingApps;
270 
271         // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
272         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
273                 w -> w.getRemoteAnimationDefinition() != null
274                         && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
275         if (result != null) {
276             return result;
277         }
278         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
279                 w -> w.fillsParent() && w.findMainWindow() != null);
280         if (result != null) {
281             return result;
282         }
283         return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
284                 w -> w.findMainWindow() != null);
285     }
286 
287     /**
288      * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
289      *         of apps in {@code array1}, {@code array2}, and {@code array3}.
290      */
collectActivityTypes(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3)291     private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
292             ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3) {
293         final ArraySet<Integer> result = new ArraySet<>();
294         for (int i = array1.size() - 1; i >= 0; i--) {
295             result.add(array1.valueAt(i).getActivityType());
296         }
297         for (int i = array2.size() - 1; i >= 0; i--) {
298             result.add(array2.valueAt(i).getActivityType());
299         }
300         for (int i = array3.size() - 1; i >= 0; i--) {
301             result.add(array3.valueAt(i).getActivityType());
302         }
303         return result;
304     }
305 
lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3, Predicate<AppWindowToken> filter)306     private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
307             ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3,
308             Predicate<AppWindowToken> filter) {
309         final int array2base = array1.size();
310         final int array3base = array2.size() + array2base;
311         final int count = array3base + array3.size();
312         int bestPrefixOrderIndex = Integer.MIN_VALUE;
313         AppWindowToken bestToken = null;
314         for (int i = 0; i < count; i++) {
315             final AppWindowToken wtoken = i < array2base
316                     ? array1.valueAt(i)
317                     : (i < array3base
318                             ? array2.valueAt(i - array2base)
319                             : array3.valueAt(i - array3base));
320             final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
321             if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
322                 bestPrefixOrderIndex = prefixOrderIndex;
323                 bestToken = wtoken;
324             }
325         }
326         return bestToken;
327     }
328 
329     private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
330         for (int i = apps.size() - 1; i >= 0; i--) {
331             if (apps.valueAt(i).mVoiceInteraction) {
332                 return true;
333             }
334         }
335         return false;
336     }
337 
handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction)338     private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
339         final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
340         final int appsCount = openingApps.size();
341         for (int i = 0; i < appsCount; i++) {
342             AppWindowToken wtoken = openingApps.valueAt(i);
343             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
344 
345             if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
346                 // This token isn't going to be animating. Add it to the list of tokens to
347                 // be notified of app transition complete since the notification will not be
348                 // sent be the app window animator.
349                 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
350             }
351             wtoken.updateReportedVisibilityLocked();
352             wtoken.waitingToShow = false;
353             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
354                     ">>> OPEN TRANSACTION handleAppTransitionReady()");
355             mService.openSurfaceTransaction();
356             try {
357                 wtoken.showAllWindowsLocked();
358             } finally {
359                 mService.closeSurfaceTransaction("handleAppTransitionReady");
360                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
361                         "<<< CLOSE TRANSACTION handleAppTransitionReady()");
362             }
363 
364             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
365                 wtoken.attachThumbnailAnimation();
366             } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
367                 wtoken.attachCrossProfileAppsThumbnailAnimation();
368             }
369         }
370     }
371 
handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction)372     private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
373         final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
374         final int appsCount = closingApps.size();
375         for (int i = 0; i < appsCount; i++) {
376             AppWindowToken wtoken = closingApps.valueAt(i);
377 
378             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
379             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
380             //       animating?
381             wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
382             wtoken.updateReportedVisibilityLocked();
383             // Force the allDrawn flag, because we want to start
384             // this guy's animations regardless of whether it's
385             // gotten drawn.
386             wtoken.allDrawn = true;
387             wtoken.deferClearAllDrawn = false;
388             // Ensure that apps that are mid-starting are also scheduled to have their
389             // starting windows removed after the animation is complete
390             if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
391                 wtoken.removeStartingWindow();
392             }
393 
394             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
395                 wtoken.attachThumbnailAnimation();
396             }
397         }
398     }
399 
handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction)400     private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
401         final ArraySet<AppWindowToken> apps = mDisplayContent.mChangingApps;
402         final int appsCount = apps.size();
403         for (int i = 0; i < appsCount; i++) {
404             AppWindowToken wtoken = apps.valueAt(i);
405             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now changing app" + wtoken);
406             wtoken.cancelAnimationOnly();
407             wtoken.applyAnimationLocked(null, transit, true, false);
408             wtoken.updateReportedVisibilityLocked();
409             mService.openSurfaceTransaction();
410             try {
411                 wtoken.showAllWindowsLocked();
412             } finally {
413                 mService.closeSurfaceTransaction("handleChangingApps");
414             }
415         }
416     }
417 
handleNonAppWindowsInTransition(int transit, int flags)418     private void handleNonAppWindowsInTransition(int transit, int flags) {
419         if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
420             if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
421                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
422                 Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
423                         (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
424                 if (anim != null) {
425                     mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
426                 }
427             }
428         }
429         if (transit == TRANSIT_KEYGUARD_GOING_AWAY
430                 || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
431             mDisplayContent.startKeyguardExitOnNonAppWindows(
432                     transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
433                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
434         }
435     }
436 
transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons)437     private boolean transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons) {
438         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
439                 "Checking " + apps.size() + " opening apps (frozen="
440                         + mService.mDisplayFrozen + " timeout="
441                         + mDisplayContent.mAppTransition.isTimeout() + ")...");
442         final ScreenRotationAnimation screenRotationAnimation =
443                 mService.mAnimator.getScreenRotationAnimationLocked(
444                         Display.DEFAULT_DISPLAY);
445 
446         if (!mDisplayContent.mAppTransition.isTimeout()) {
447             // Imagine the case where we are changing orientation due to an app transition, but a
448             // previous orientation change is still in progress. We won't process the orientation
449             // change for our transition because we need to wait for the rotation animation to
450             // finish.
451             // If we start the app transition at this point, we will interrupt it halfway with a
452             // new rotation animation after the old one finally finishes. It's better to defer the
453             // app transition.
454             if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
455                     mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
456                 if (DEBUG_APP_TRANSITIONS) {
457                     Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
458                 }
459                 return false;
460             }
461             for (int i = 0; i < apps.size(); i++) {
462                 AppWindowToken wtoken = apps.valueAt(i);
463                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
464                         "Check opening app=" + wtoken + ": allDrawn="
465                                 + wtoken.allDrawn + " startingDisplayed="
466                                 + wtoken.startingDisplayed + " startingMoved="
467                                 + wtoken.startingMoved + " isRelaunching()="
468                                 + wtoken.isRelaunching() + " startingWindow="
469                                 + wtoken.startingWindow);
470 
471 
472                 final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
473                 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
474                     return false;
475                 }
476                 final int windowingMode = wtoken.getWindowingMode();
477                 if (allDrawn) {
478                     outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
479                 } else {
480                     outReasons.put(windowingMode,
481                             wtoken.mStartingData instanceof SplashScreenStartingData
482                                     ? APP_TRANSITION_SPLASH_SCREEN
483                                     : APP_TRANSITION_SNAPSHOT);
484                 }
485             }
486 
487             // We also need to wait for the specs to be fetched, if needed.
488             if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
489                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
490                 return false;
491             }
492 
493             if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
494                 if (DEBUG_APP_TRANSITIONS) {
495                     Slog.v(TAG, "unknownApps is not empty: "
496                             + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
497                 }
498                 return false;
499             }
500 
501             // If the wallpaper is visible, we need to check it's ready too.
502             boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
503                     mWallpaperControllerLocked.wallpaperTransitionReady();
504             if (wallpaperReady) {
505                 return true;
506             }
507             return false;
508         }
509         return true;
510     }
511 
maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, boolean closingAppHasWallpaper)512     private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
513             boolean closingAppHasWallpaper) {
514         // Given no app transition pass it through instead of a wallpaper transition.
515         // Never convert the crashing transition.
516         // Never update the transition for the wallpaper if we are just docking from recents
517         // Never convert a change transition since the top activity isn't changing and will likely
518         // still be above an opening wallpaper.
519         if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
520                 || transit == TRANSIT_DOCK_TASK_FROM_RECENTS
521                 || AppTransition.isChangeTransit(transit)) {
522             return transit;
523         }
524 
525         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
526         final boolean showWallpaper = wallpaperTarget != null
527                 && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
528         // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
529         // don't consider upgrading to wallpaper transition.
530         final WindowState oldWallpaper =
531                 (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
532                         ? null
533                         : wallpaperTarget;
534         final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
535         final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
536         final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
537                 false /* ignoreHidden */);
538         final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps,
539                 true /* ignoreHidden */);
540 
541         boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
542         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
543                 "New wallpaper target=" + wallpaperTarget
544                         + ", oldWallpaper=" + oldWallpaper
545                         + ", openingApps=" + openingApps
546                         + ", closingApps=" + closingApps);
547 
548         if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
549             transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
550             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
551                     "New transit: " + AppTransition.appTransitionToString(transit));
552         }
553         // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
554         // relies on the fact that we always execute a Keyguard transition after preparing one.
555         else if (!isKeyguardGoingAwayTransit(transit)) {
556             if (closingAppHasWallpaper && openingAppHasWallpaper) {
557                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
558                 switch (transit) {
559                     case TRANSIT_ACTIVITY_OPEN:
560                     case TRANSIT_TASK_OPEN:
561                     case TRANSIT_TASK_TO_FRONT:
562                         transit = TRANSIT_WALLPAPER_INTRA_OPEN;
563                         break;
564                     case TRANSIT_ACTIVITY_CLOSE:
565                     case TRANSIT_TASK_CLOSE:
566                     case TRANSIT_TASK_TO_BACK:
567                         transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
568                         break;
569                 }
570                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
571                         "New transit: " + AppTransition.appTransitionToString(transit));
572             } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
573                     && !openingApps.contains(oldWallpaper.mAppToken)
574                     && closingApps.contains(oldWallpaper.mAppToken)
575                     && topClosingApp == oldWallpaper.mAppToken) {
576                 // We are transitioning from an activity with a wallpaper to one without.
577                 transit = TRANSIT_WALLPAPER_CLOSE;
578                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
579                         + AppTransition.appTransitionToString(transit));
580             } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
581                     && openingApps.contains(wallpaperTarget.mAppToken)
582                     && topOpeningApp == wallpaperTarget.mAppToken
583                     && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
584                 // We are transitioning from an activity without
585                 // a wallpaper to now showing the wallpaper
586                 transit = TRANSIT_WALLPAPER_OPEN;
587                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
588                         + AppTransition.appTransitionToString(transit));
589             }
590         }
591         return transit;
592     }
593 
594     /**
595      * There are cases where we open/close a new task/activity, but in reality only a translucent
596      * activity on top of existing activities is opening/closing. For that one, we have a different
597      * animation because non of the task/activity animations actually work well with translucent
598      * apps.
599      *
600      * @param transit The current transition type.
601      * @return The current transition type or
602      *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
603      *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
604      *         situation.
605      */
606     @VisibleForTesting
maybeUpdateTransitToTranslucentAnim(int transit)607     int maybeUpdateTransitToTranslucentAnim(int transit) {
608         if (AppTransition.isChangeTransit(transit)) {
609             // There's no special animation to handle change animations with translucent apps
610             return transit;
611         }
612         final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
613                 || AppTransition.isActivityTransit(transit);
614         boolean allOpeningVisible = true;
615         boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
616         for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
617             final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i);
618             if (!token.isVisible()) {
619                 allOpeningVisible = false;
620                 if (token.fillsParent()) {
621                     allTranslucentOpeningApps = false;
622                 }
623             }
624         }
625         boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty();
626         for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
627             if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) {
628                 allTranslucentClosingApps = false;
629                 break;
630             }
631         }
632 
633         if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
634             return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
635         }
636         if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
637             return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
638         }
639         return transit;
640     }
641 
642     /**
643      * Identifies whether the current transition occurs within a single task or not. This is used
644      * to determine whether animations should be clipped to the task bounds instead of stack bounds.
645      */
646     @VisibleForTesting
isTransitWithinTask(int transit, Task task)647     boolean isTransitWithinTask(int transit, Task task) {
648         if (task == null
649                 || !mDisplayContent.mChangingApps.isEmpty()) {
650             // if there is no task, then we can't constrain to the task.
651             // if anything is changing, it can animate outside its task.
652             return false;
653         }
654         if (!(transit == TRANSIT_ACTIVITY_OPEN
655                 || transit == TRANSIT_ACTIVITY_CLOSE
656                 || transit == TRANSIT_ACTIVITY_RELAUNCH)) {
657             // only activity-level transitions will be within-task.
658             return false;
659         }
660         // check that all components are in the task.
661         for (AppWindowToken activity : mDisplayContent.mOpeningApps) {
662             Task activityTask = activity.getTask();
663             if (activityTask != task) {
664                 return false;
665             }
666         }
667         for (AppWindowToken activity : mDisplayContent.mClosingApps) {
668             if (activity.getTask() != task) {
669                 return false;
670             }
671         }
672         return true;
673     }
674 
canBeWallpaperTarget(ArraySet<AppWindowToken> apps)675     private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
676         for (int i = apps.size() - 1; i >= 0; i--) {
677             if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
678                 return true;
679             }
680         }
681         return false;
682     }
683 
684     /**
685      * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
686      * compare z-order.
687      *
688      * @param apps The list of apps to search.
689      * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
690      * @return The top {@link AppWindowToken}.
691      */
getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden)692     private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
693         int topPrefixOrderIndex = Integer.MIN_VALUE;
694         AppWindowToken topApp = null;
695         for (int i = apps.size() - 1; i >= 0; i--) {
696             final AppWindowToken app = apps.valueAt(i);
697             if (ignoreHidden && app.isHidden()) {
698                 continue;
699             }
700             final int prefixOrderIndex = app.getPrefixOrderIndex();
701             if (prefixOrderIndex > topPrefixOrderIndex) {
702                 topPrefixOrderIndex = prefixOrderIndex;
703                 topApp = app;
704             }
705         }
706         return topApp;
707     }
708 
processApplicationsAnimatingInPlace(int transit)709     private void processApplicationsAnimatingInPlace(int transit) {
710         if (transit == TRANSIT_TASK_IN_PLACE) {
711             // Find the focused window
712             final WindowState win = mDisplayContent.findFocusedWindow();
713             if (win != null) {
714                 final AppWindowToken wtoken = win.mAppToken;
715                 if (DEBUG_APP_TRANSITIONS)
716                     Slog.v(TAG, "Now animating app in place " + wtoken);
717                 wtoken.cancelAnimation();
718                 wtoken.applyAnimationLocked(null, transit, false, false);
719                 wtoken.updateReportedVisibilityLocked();
720                 wtoken.showAllWindowsLocked();
721             }
722         }
723     }
724 }
725