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