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