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