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