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_TO_SHADE; 27 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 28 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; 29 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 30 import static android.view.WindowManager.TRANSIT_NONE; 31 import static android.view.WindowManager.TRANSIT_TASK_CLOSE; 32 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; 33 import static android.view.WindowManager.TRANSIT_TASK_OPEN; 34 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; 35 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; 36 import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; 37 import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; 38 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; 39 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; 40 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; 41 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; 42 43 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; 44 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; 45 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; 46 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; 47 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; 48 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; 49 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 50 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 51 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 52 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 53 54 import android.os.SystemClock; 55 import android.os.Trace; 56 import android.util.ArraySet; 57 import android.util.Slog; 58 import android.util.SparseIntArray; 59 import android.view.Display; 60 import android.view.RemoteAnimationAdapter; 61 import android.view.RemoteAnimationDefinition; 62 import android.view.WindowManager; 63 import android.view.WindowManager.LayoutParams; 64 import android.view.animation.Animation; 65 66 import com.android.internal.annotations.VisibleForTesting; 67 68 import java.util.function.Predicate; 69 70 71 /** 72 * Checks for app transition readiness, resolves animation attributes and performs visibility 73 * change for apps that animate as part of an app transition. 74 */ 75 public class AppTransitionController { 76 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM; 77 private final WindowManagerService mService; 78 private final DisplayContent mDisplayContent; 79 private final WallpaperController mWallpaperControllerLocked; 80 private RemoteAnimationDefinition mRemoteAnimationDefinition = null; 81 82 private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); 83 AppTransitionController(WindowManagerService service, DisplayContent displayContent)84 AppTransitionController(WindowManagerService service, DisplayContent displayContent) { 85 mService = service; 86 mDisplayContent = displayContent; 87 mWallpaperControllerLocked = mDisplayContent.mWallpaperController; 88 } 89 registerRemoteAnimations(RemoteAnimationDefinition definition)90 void registerRemoteAnimations(RemoteAnimationDefinition definition) { 91 mRemoteAnimationDefinition = definition; 92 } 93 94 /** 95 * Handle application transition for given display. 96 */ handleAppTransitionReady()97 void handleAppTransitionReady() { 98 mTempTransitionReasons.clear(); 99 if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) 100 || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) { 101 return; 102 } 103 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); 104 105 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); 106 final AppTransition appTransition = mDisplayContent.mAppTransition; 107 int transit = appTransition.getAppTransition(); 108 if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { 109 transit = WindowManager.TRANSIT_UNSET; 110 } 111 mDisplayContent.mSkipAppTransitionAnimation = false; 112 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); 113 114 appTransition.removeAppTransitionTimeoutCallbacks(); 115 116 mDisplayContent.mWallpaperMayChange = false; 117 118 int appCount = mDisplayContent.mOpeningApps.size(); 119 for (int i = 0; i < appCount; ++i) { 120 // Clearing the mAnimatingExit flag before entering animation. It's set to true if app 121 // window is removed, or window relayout to invisible. This also affects window 122 // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the 123 // transition selection depends on wallpaper target visibility. 124 mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags(); 125 } 126 appCount = mDisplayContent.mChangingApps.size(); 127 for (int i = 0; i < appCount; ++i) { 128 // Clearing for same reason as above. 129 mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags(); 130 } 131 132 // Adjust wallpaper before we pull the lower/upper target, since pending changes 133 // (like the clearAnimatingFlags() above) might affect wallpaper target result. 134 // Or, the opening app window should be a wallpaper target. 135 mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( 136 mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps); 137 138 // Determine if closing and opening app token sets are wallpaper targets, in which case 139 // special animations are needed. 140 final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; 141 final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps) 142 && hasWallpaperTarget; 143 final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps) 144 && hasWallpaperTarget; 145 146 transit = maybeUpdateTransitToTranslucentAnim(transit); 147 transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, 148 closingAppHasWallpaper); 149 150 // Find the layout params of the top-most application window in the tokens, which is 151 // what will control the animation theme. If all closing windows are obscured, then there is 152 // no need to do an animation. This is the case, for example, when this transition is being 153 // done behind a dream window. 154 final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, 155 mDisplayContent.mClosingApps, mDisplayContent.mChangingApps); 156 final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw(); 157 final AppWindowToken animLpToken = allowAnimations 158 ? findAnimLayoutParamsToken(transit, activityTypes) 159 : null; 160 final AppWindowToken topOpeningApp = allowAnimations 161 ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */) 162 : null; 163 final AppWindowToken topClosingApp = allowAnimations 164 ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */) 165 : null; 166 final AppWindowToken topChangingApp = allowAnimations 167 ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */) 168 : null; 169 final WindowManager.LayoutParams animLp = getAnimLp(animLpToken); 170 overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes); 171 172 final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps) 173 || containsVoiceInteraction(mDisplayContent.mOpeningApps) 174 || containsVoiceInteraction(mDisplayContent.mChangingApps); 175 176 final int layoutRedo; 177 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 178 try { 179 processApplicationsAnimatingInPlace(transit); 180 181 handleClosingApps(transit, animLp, voiceInteraction); 182 handleOpeningApps(transit, animLp, voiceInteraction); 183 handleChangingApps(transit, animLp, voiceInteraction); 184 185 appTransition.setLastAppTransition(transit, topOpeningApp, 186 topClosingApp, topChangingApp); 187 188 final int flags = appTransition.getTransitFlags(); 189 layoutRedo = appTransition.goodToGo(transit, topOpeningApp, 190 mDisplayContent.mOpeningApps); 191 handleNonAppWindowsInTransition(transit, flags); 192 appTransition.postAnimationCallback(); 193 appTransition.clear(); 194 } finally { 195 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 196 } 197 198 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent); 199 200 mDisplayContent.mOpeningApps.clear(); 201 mDisplayContent.mClosingApps.clear(); 202 mDisplayContent.mChangingApps.clear(); 203 mDisplayContent.mUnknownAppVisibilityController.clear(); 204 205 // This has changed the visibility of windows, so perform 206 // a new layout to get them all up-to-date. 207 mDisplayContent.setLayoutNeeded(); 208 209 mDisplayContent.computeImeTarget(true /* updateImeTarget */); 210 211 mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(), 212 SystemClock.uptimeMillis()); 213 214 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 215 216 mDisplayContent.pendingLayoutChanges |= 217 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; 218 } 219 getAnimLp(AppWindowToken wtoken)220 private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) { 221 final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null; 222 return mainWindow != null ? mainWindow.mAttrs : null; 223 } 224 getRemoteAnimationOverride(AppWindowToken animLpToken, int transit, ArraySet<Integer> activityTypes)225 RemoteAnimationAdapter getRemoteAnimationOverride(AppWindowToken animLpToken, int transit, 226 ArraySet<Integer> activityTypes) { 227 final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); 228 if (definition != null) { 229 final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); 230 if (adapter != null) { 231 return adapter; 232 } 233 } 234 if (mRemoteAnimationDefinition == null) { 235 return null; 236 } 237 return mRemoteAnimationDefinition.getAdapter(transit, activityTypes); 238 } 239 240 /** 241 * Overrides the pending transition with the remote animation defined for the transition in the 242 * set of defined remote animations in the app window token. 243 */ overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, ArraySet<Integer> activityTypes)244 private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, 245 ArraySet<Integer> activityTypes) { 246 if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) { 247 // The crash transition has higher priority than any involved remote animations. 248 return; 249 } 250 if (animLpToken == null) { 251 return; 252 } 253 final RemoteAnimationAdapter adapter = 254 getRemoteAnimationOverride(animLpToken, transit, activityTypes); 255 if (adapter != null) { 256 animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote( 257 adapter); 258 } 259 } 260 261 /** 262 * @return The window token that determines the animation theme. 263 */ findAnimLayoutParamsToken(@indowManager.TransitionType int transit, ArraySet<Integer> activityTypes)264 private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit, 265 ArraySet<Integer> activityTypes) { 266 AppWindowToken result; 267 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; 268 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; 269 final ArraySet<AppWindowToken> changingApps = mDisplayContent.mChangingApps; 270 271 // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. 272 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 273 w -> w.getRemoteAnimationDefinition() != null 274 && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); 275 if (result != null) { 276 return result; 277 } 278 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 279 w -> w.fillsParent() && w.findMainWindow() != null); 280 if (result != null) { 281 return result; 282 } 283 return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 284 w -> w.findMainWindow() != null); 285 } 286 287 /** 288 * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set 289 * of apps in {@code array1}, {@code array2}, and {@code array3}. 290 */ collectActivityTypes(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3)291 private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1, 292 ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3) { 293 final ArraySet<Integer> result = new ArraySet<>(); 294 for (int i = array1.size() - 1; i >= 0; i--) { 295 result.add(array1.valueAt(i).getActivityType()); 296 } 297 for (int i = array2.size() - 1; i >= 0; i--) { 298 result.add(array2.valueAt(i).getActivityType()); 299 } 300 for (int i = array3.size() - 1; i >= 0; i--) { 301 result.add(array3.valueAt(i).getActivityType()); 302 } 303 return result; 304 } 305 lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3, Predicate<AppWindowToken> filter)306 private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, 307 ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3, 308 Predicate<AppWindowToken> filter) { 309 final int array2base = array1.size(); 310 final int array3base = array2.size() + array2base; 311 final int count = array3base + array3.size(); 312 int bestPrefixOrderIndex = Integer.MIN_VALUE; 313 AppWindowToken bestToken = null; 314 for (int i = 0; i < count; i++) { 315 final AppWindowToken wtoken = i < array2base 316 ? array1.valueAt(i) 317 : (i < array3base 318 ? array2.valueAt(i - array2base) 319 : array3.valueAt(i - array3base)); 320 final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); 321 if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { 322 bestPrefixOrderIndex = prefixOrderIndex; 323 bestToken = wtoken; 324 } 325 } 326 return bestToken; 327 } 328 329 private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) { 330 for (int i = apps.size() - 1; i >= 0; i--) { 331 if (apps.valueAt(i).mVoiceInteraction) { 332 return true; 333 } 334 } 335 return false; 336 } 337 handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction)338 private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) { 339 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; 340 final int appsCount = openingApps.size(); 341 for (int i = 0; i < appsCount; i++) { 342 AppWindowToken wtoken = openingApps.valueAt(i); 343 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); 344 345 if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) { 346 // This token isn't going to be animating. Add it to the list of tokens to 347 // be notified of app transition complete since the notification will not be 348 // sent be the app window animator. 349 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); 350 } 351 wtoken.updateReportedVisibilityLocked(); 352 wtoken.waitingToShow = false; 353 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 354 ">>> OPEN TRANSACTION handleAppTransitionReady()"); 355 mService.openSurfaceTransaction(); 356 try { 357 wtoken.showAllWindowsLocked(); 358 } finally { 359 mService.closeSurfaceTransaction("handleAppTransitionReady"); 360 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 361 "<<< CLOSE TRANSACTION handleAppTransitionReady()"); 362 } 363 364 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) { 365 wtoken.attachThumbnailAnimation(); 366 } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { 367 wtoken.attachCrossProfileAppsThumbnailAnimation(); 368 } 369 } 370 } 371 handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction)372 private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) { 373 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; 374 final int appsCount = closingApps.size(); 375 for (int i = 0; i < appsCount; i++) { 376 AppWindowToken wtoken = closingApps.valueAt(i); 377 378 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); 379 // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not 380 // animating? 381 wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction); 382 wtoken.updateReportedVisibilityLocked(); 383 // Force the allDrawn flag, because we want to start 384 // this guy's animations regardless of whether it's 385 // gotten drawn. 386 wtoken.allDrawn = true; 387 wtoken.deferClearAllDrawn = false; 388 // Ensure that apps that are mid-starting are also scheduled to have their 389 // starting windows removed after the animation is complete 390 if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) { 391 wtoken.removeStartingWindow(); 392 } 393 394 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { 395 wtoken.attachThumbnailAnimation(); 396 } 397 } 398 } 399 handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction)400 private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) { 401 final ArraySet<AppWindowToken> apps = mDisplayContent.mChangingApps; 402 final int appsCount = apps.size(); 403 for (int i = 0; i < appsCount; i++) { 404 AppWindowToken wtoken = apps.valueAt(i); 405 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now changing app" + wtoken); 406 wtoken.cancelAnimationOnly(); 407 wtoken.applyAnimationLocked(null, transit, true, false); 408 wtoken.updateReportedVisibilityLocked(); 409 mService.openSurfaceTransaction(); 410 try { 411 wtoken.showAllWindowsLocked(); 412 } finally { 413 mService.closeSurfaceTransaction("handleChangingApps"); 414 } 415 } 416 } 417 handleNonAppWindowsInTransition(int transit, int flags)418 private void handleNonAppWindowsInTransition(int transit, int flags) { 419 if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { 420 if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 421 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) { 422 Animation anim = mService.mPolicy.createKeyguardWallpaperExit( 423 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 424 if (anim != null) { 425 mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); 426 } 427 } 428 } 429 if (transit == TRANSIT_KEYGUARD_GOING_AWAY 430 || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { 431 mDisplayContent.startKeyguardExitOnNonAppWindows( 432 transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, 433 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 434 } 435 } 436 transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons)437 private boolean transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons) { 438 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 439 "Checking " + apps.size() + " opening apps (frozen=" 440 + mService.mDisplayFrozen + " timeout=" 441 + mDisplayContent.mAppTransition.isTimeout() + ")..."); 442 final ScreenRotationAnimation screenRotationAnimation = 443 mService.mAnimator.getScreenRotationAnimationLocked( 444 Display.DEFAULT_DISPLAY); 445 446 if (!mDisplayContent.mAppTransition.isTimeout()) { 447 // Imagine the case where we are changing orientation due to an app transition, but a 448 // previous orientation change is still in progress. We won't process the orientation 449 // change for our transition because we need to wait for the rotation animation to 450 // finish. 451 // If we start the app transition at this point, we will interrupt it halfway with a 452 // new rotation animation after the old one finally finishes. It's better to defer the 453 // app transition. 454 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() && 455 mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) { 456 if (DEBUG_APP_TRANSITIONS) { 457 Slog.v(TAG, "Delaying app transition for screen rotation animation to finish"); 458 } 459 return false; 460 } 461 for (int i = 0; i < apps.size(); i++) { 462 AppWindowToken wtoken = apps.valueAt(i); 463 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 464 "Check opening app=" + wtoken + ": allDrawn=" 465 + wtoken.allDrawn + " startingDisplayed=" 466 + wtoken.startingDisplayed + " startingMoved=" 467 + wtoken.startingMoved + " isRelaunching()=" 468 + wtoken.isRelaunching() + " startingWindow=" 469 + wtoken.startingWindow); 470 471 472 final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); 473 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { 474 return false; 475 } 476 final int windowingMode = wtoken.getWindowingMode(); 477 if (allDrawn) { 478 outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); 479 } else { 480 outReasons.put(windowingMode, 481 wtoken.mStartingData instanceof SplashScreenStartingData 482 ? APP_TRANSITION_SPLASH_SCREEN 483 : APP_TRANSITION_SNAPSHOT); 484 } 485 } 486 487 // We also need to wait for the specs to be fetched, if needed. 488 if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) { 489 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true"); 490 return false; 491 } 492 493 if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) { 494 if (DEBUG_APP_TRANSITIONS) { 495 Slog.v(TAG, "unknownApps is not empty: " 496 + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage()); 497 } 498 return false; 499 } 500 501 // If the wallpaper is visible, we need to check it's ready too. 502 boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || 503 mWallpaperControllerLocked.wallpaperTransitionReady(); 504 if (wallpaperReady) { 505 return true; 506 } 507 return false; 508 } 509 return true; 510 } 511 maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, boolean closingAppHasWallpaper)512 private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, 513 boolean closingAppHasWallpaper) { 514 // Given no app transition pass it through instead of a wallpaper transition. 515 // Never convert the crashing transition. 516 // Never update the transition for the wallpaper if we are just docking from recents 517 // Never convert a change transition since the top activity isn't changing and will likely 518 // still be above an opening wallpaper. 519 if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE 520 || transit == TRANSIT_DOCK_TASK_FROM_RECENTS 521 || AppTransition.isChangeTransit(transit)) { 522 return transit; 523 } 524 525 final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); 526 final boolean showWallpaper = wallpaperTarget != null 527 && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; 528 // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, 529 // don't consider upgrading to wallpaper transition. 530 final WindowState oldWallpaper = 531 (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) 532 ? null 533 : wallpaperTarget; 534 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; 535 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; 536 final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps, 537 false /* ignoreHidden */); 538 final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps, 539 true /* ignoreHidden */); 540 541 boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); 542 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 543 "New wallpaper target=" + wallpaperTarget 544 + ", oldWallpaper=" + oldWallpaper 545 + ", openingApps=" + openingApps 546 + ", closingApps=" + closingApps); 547 548 if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { 549 transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 550 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 551 "New transit: " + AppTransition.appTransitionToString(transit)); 552 } 553 // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic 554 // relies on the fact that we always execute a Keyguard transition after preparing one. 555 else if (!isKeyguardGoingAwayTransit(transit)) { 556 if (closingAppHasWallpaper && openingAppHasWallpaper) { 557 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); 558 switch (transit) { 559 case TRANSIT_ACTIVITY_OPEN: 560 case TRANSIT_TASK_OPEN: 561 case TRANSIT_TASK_TO_FRONT: 562 transit = TRANSIT_WALLPAPER_INTRA_OPEN; 563 break; 564 case TRANSIT_ACTIVITY_CLOSE: 565 case TRANSIT_TASK_CLOSE: 566 case TRANSIT_TASK_TO_BACK: 567 transit = TRANSIT_WALLPAPER_INTRA_CLOSE; 568 break; 569 } 570 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 571 "New transit: " + AppTransition.appTransitionToString(transit)); 572 } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty() 573 && !openingApps.contains(oldWallpaper.mAppToken) 574 && closingApps.contains(oldWallpaper.mAppToken) 575 && topClosingApp == oldWallpaper.mAppToken) { 576 // We are transitioning from an activity with a wallpaper to one without. 577 transit = TRANSIT_WALLPAPER_CLOSE; 578 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " 579 + AppTransition.appTransitionToString(transit)); 580 } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() 581 && openingApps.contains(wallpaperTarget.mAppToken) 582 && topOpeningApp == wallpaperTarget.mAppToken 583 && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) { 584 // We are transitioning from an activity without 585 // a wallpaper to now showing the wallpaper 586 transit = TRANSIT_WALLPAPER_OPEN; 587 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " 588 + AppTransition.appTransitionToString(transit)); 589 } 590 } 591 return transit; 592 } 593 594 /** 595 * There are cases where we open/close a new task/activity, but in reality only a translucent 596 * activity on top of existing activities is opening/closing. For that one, we have a different 597 * animation because non of the task/activity animations actually work well with translucent 598 * apps. 599 * 600 * @param transit The current transition type. 601 * @return The current transition type or 602 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/ 603 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the 604 * situation. 605 */ 606 @VisibleForTesting maybeUpdateTransitToTranslucentAnim(int transit)607 int maybeUpdateTransitToTranslucentAnim(int transit) { 608 if (AppTransition.isChangeTransit(transit)) { 609 // There's no special animation to handle change animations with translucent apps 610 return transit; 611 } 612 final boolean taskOrActivity = AppTransition.isTaskTransit(transit) 613 || AppTransition.isActivityTransit(transit); 614 boolean allOpeningVisible = true; 615 boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty(); 616 for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) { 617 final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i); 618 if (!token.isVisible()) { 619 allOpeningVisible = false; 620 if (token.fillsParent()) { 621 allTranslucentOpeningApps = false; 622 } 623 } 624 } 625 boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty(); 626 for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) { 627 if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) { 628 allTranslucentClosingApps = false; 629 break; 630 } 631 } 632 633 if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) { 634 return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; 635 } 636 if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) { 637 return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; 638 } 639 return transit; 640 } 641 642 /** 643 * Identifies whether the current transition occurs within a single task or not. This is used 644 * to determine whether animations should be clipped to the task bounds instead of stack bounds. 645 */ 646 @VisibleForTesting isTransitWithinTask(int transit, Task task)647 boolean isTransitWithinTask(int transit, Task task) { 648 if (task == null 649 || !mDisplayContent.mChangingApps.isEmpty()) { 650 // if there is no task, then we can't constrain to the task. 651 // if anything is changing, it can animate outside its task. 652 return false; 653 } 654 if (!(transit == TRANSIT_ACTIVITY_OPEN 655 || transit == TRANSIT_ACTIVITY_CLOSE 656 || transit == TRANSIT_ACTIVITY_RELAUNCH)) { 657 // only activity-level transitions will be within-task. 658 return false; 659 } 660 // check that all components are in the task. 661 for (AppWindowToken activity : mDisplayContent.mOpeningApps) { 662 Task activityTask = activity.getTask(); 663 if (activityTask != task) { 664 return false; 665 } 666 } 667 for (AppWindowToken activity : mDisplayContent.mClosingApps) { 668 if (activity.getTask() != task) { 669 return false; 670 } 671 } 672 return true; 673 } 674 canBeWallpaperTarget(ArraySet<AppWindowToken> apps)675 private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) { 676 for (int i = apps.size() - 1; i >= 0; i--) { 677 if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { 678 return true; 679 } 680 } 681 return false; 682 } 683 684 /** 685 * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to 686 * compare z-order. 687 * 688 * @param apps The list of apps to search. 689 * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}. 690 * @return The top {@link AppWindowToken}. 691 */ getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden)692 private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) { 693 int topPrefixOrderIndex = Integer.MIN_VALUE; 694 AppWindowToken topApp = null; 695 for (int i = apps.size() - 1; i >= 0; i--) { 696 final AppWindowToken app = apps.valueAt(i); 697 if (ignoreHidden && app.isHidden()) { 698 continue; 699 } 700 final int prefixOrderIndex = app.getPrefixOrderIndex(); 701 if (prefixOrderIndex > topPrefixOrderIndex) { 702 topPrefixOrderIndex = prefixOrderIndex; 703 topApp = app; 704 } 705 } 706 return topApp; 707 } 708 processApplicationsAnimatingInPlace(int transit)709 private void processApplicationsAnimatingInPlace(int transit) { 710 if (transit == TRANSIT_TASK_IN_PLACE) { 711 // Find the focused window 712 final WindowState win = mDisplayContent.findFocusedWindow(); 713 if (win != null) { 714 final AppWindowToken wtoken = win.mAppToken; 715 if (DEBUG_APP_TRANSITIONS) 716 Slog.v(TAG, "Now animating app in place " + wtoken); 717 wtoken.cancelAnimation(); 718 wtoken.applyAnimationLocked(null, transit, false, false); 719 wtoken.updateReportedVisibilityLocked(); 720 wtoken.showAllWindowsLocked(); 721 } 722 } 723 } 724 } 725