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