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