1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT; 20 import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN; 21 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; 22 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_TRANSLUCENT_ACTIVITY_CLOSE; 26 import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; 27 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; 28 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; 29 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; 30 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; 31 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; 32 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; 33 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 34 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; 35 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 36 import static android.view.WindowManager.TRANSIT_NONE; 37 import static android.view.WindowManager.TRANSIT_TASK_CLOSE; 38 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; 39 import static android.view.WindowManager.TRANSIT_TASK_OPEN; 40 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; 41 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; 42 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; 43 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; 44 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; 45 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; 46 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; 47 import static com.android.server.wm.AppTransition.isTaskTransit; 48 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; 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 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING; 54 import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE; 55 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; 56 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES; 57 58 import android.app.WindowConfiguration; 59 import android.os.Debug; 60 import android.os.Trace; 61 import android.util.ArraySet; 62 import android.util.Slog; 63 import android.util.SparseIntArray; 64 import android.view.Display; 65 import android.view.RemoteAnimationAdapter; 66 import android.view.RemoteAnimationDefinition; 67 import android.view.WindowManager; 68 import android.view.WindowManager.LayoutParams; 69 import android.view.WindowManager.TransitionType; 70 import android.view.animation.Animation; 71 72 import com.android.internal.annotations.VisibleForTesting; 73 import com.android.server.wm.WindowManagerService.H; 74 75 import java.io.PrintWriter; 76 import java.util.function.Predicate; 77 78 /** 79 * Positions windows and their surfaces. 80 * 81 * It sets positions of windows by calculating their frames and then applies this by positioning 82 * surfaces according to these frames. Z layer is still assigned withing WindowManagerService. 83 */ 84 class WindowSurfacePlacer { 85 private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM; 86 private final WindowManagerService mService; 87 private final WallpaperController mWallpaperControllerLocked; 88 89 private boolean mInLayout = false; 90 91 /** Only do a maximum of 6 repeated layouts. After that quit */ 92 private int mLayoutRepeatCount; 93 94 static final int SET_UPDATE_ROTATION = 1 << 0; 95 static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1; 96 static final int SET_FORCE_HIDING_CHANGED = 1 << 2; 97 static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3; 98 static final int SET_WALLPAPER_ACTION_PENDING = 1 << 4; 99 100 private boolean mTraversalScheduled; 101 private int mDeferDepth = 0; 102 103 private static final class LayerAndToken { 104 public int layer; 105 public AppWindowToken token; 106 } 107 private final LayerAndToken mTmpLayerAndToken = new LayerAndToken(); 108 109 private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); 110 111 private final Runnable mPerformSurfacePlacement; 112 WindowSurfacePlacer(WindowManagerService service)113 public WindowSurfacePlacer(WindowManagerService service) { 114 mService = service; 115 mWallpaperControllerLocked = mService.mRoot.mWallpaperController; 116 mPerformSurfacePlacement = () -> { 117 synchronized (mService.mWindowMap) { 118 performSurfacePlacement(); 119 } 120 }; 121 } 122 123 /** 124 * See {@link WindowManagerService#deferSurfaceLayout()} 125 */ deferLayout()126 void deferLayout() { 127 mDeferDepth++; 128 } 129 130 /** 131 * See {@link WindowManagerService#continueSurfaceLayout()} 132 */ continueLayout()133 void continueLayout() { 134 mDeferDepth--; 135 if (mDeferDepth <= 0) { 136 performSurfacePlacement(); 137 } 138 } 139 isLayoutDeferred()140 boolean isLayoutDeferred() { 141 return mDeferDepth > 0; 142 } 143 performSurfacePlacement()144 final void performSurfacePlacement() { 145 performSurfacePlacement(false /* force */); 146 } 147 performSurfacePlacement(boolean force)148 final void performSurfacePlacement(boolean force) { 149 if (mDeferDepth > 0 && !force) { 150 return; 151 } 152 int loopCount = 6; 153 do { 154 mTraversalScheduled = false; 155 performSurfacePlacementLoop(); 156 mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement); 157 loopCount--; 158 } while (mTraversalScheduled && loopCount > 0); 159 mService.mRoot.mWallpaperActionPending = false; 160 } 161 performSurfacePlacementLoop()162 private void performSurfacePlacementLoop() { 163 if (mInLayout) { 164 if (DEBUG) { 165 throw new RuntimeException("Recursive call!"); 166 } 167 Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers=" 168 + Debug.getCallers(3)); 169 return; 170 } 171 172 if (mService.mWaitingForConfig) { 173 // Our configuration has changed (most likely rotation), but we 174 // don't yet have the complete configuration to report to 175 // applications. Don't do any window layout until we have it. 176 return; 177 } 178 179 if (!mService.mDisplayReady) { 180 // Not yet initialized, nothing to do. 181 return; 182 } 183 184 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout"); 185 mInLayout = true; 186 187 boolean recoveringMemory = false; 188 if (!mService.mForceRemoves.isEmpty()) { 189 recoveringMemory = true; 190 // Wait a little bit for things to settle down, and off we go. 191 while (!mService.mForceRemoves.isEmpty()) { 192 final WindowState ws = mService.mForceRemoves.remove(0); 193 Slog.i(TAG, "Force removing: " + ws); 194 ws.removeImmediately(); 195 } 196 Slog.w(TAG, "Due to memory failure, waiting a bit for next layout"); 197 Object tmp = new Object(); 198 synchronized (tmp) { 199 try { 200 tmp.wait(250); 201 } catch (InterruptedException e) { 202 } 203 } 204 } 205 206 try { 207 mService.mRoot.performSurfacePlacement(recoveringMemory); 208 209 mInLayout = false; 210 211 if (mService.mRoot.isLayoutNeeded()) { 212 if (++mLayoutRepeatCount < 6) { 213 requestTraversal(); 214 } else { 215 Slog.e(TAG, "Performed 6 layouts in a row. Skipping"); 216 mLayoutRepeatCount = 0; 217 } 218 } else { 219 mLayoutRepeatCount = 0; 220 } 221 222 if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) { 223 mService.mH.removeMessages(REPORT_WINDOWS_CHANGE); 224 mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE); 225 } 226 } catch (RuntimeException e) { 227 mInLayout = false; 228 Slog.wtf(TAG, "Unhandled exception while laying out windows", e); 229 } 230 231 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 232 } 233 debugLayoutRepeats(final String msg, int pendingLayoutChanges)234 void debugLayoutRepeats(final String msg, int pendingLayoutChanges) { 235 if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) { 236 Slog.v(TAG, "Layouts looping: " + msg + 237 ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges)); 238 } 239 } 240 isInLayout()241 boolean isInLayout() { 242 return mInLayout; 243 } 244 245 /** 246 * @return bitmap indicating if another pass through layout must be made. 247 */ handleAppTransitionReadyLocked()248 int handleAppTransitionReadyLocked() { 249 int appsCount = mService.mOpeningApps.size(); 250 if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) { 251 return 0; 252 } 253 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); 254 255 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); 256 int transit = mService.mAppTransition.getAppTransition(); 257 if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { 258 transit = WindowManager.TRANSIT_UNSET; 259 } 260 mService.mSkipAppTransitionAnimation = false; 261 mService.mNoAnimationNotifyOnTransitionFinished.clear(); 262 263 mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); 264 265 final DisplayContent displayContent = mService.getDefaultDisplayContentLocked(); 266 267 mService.mRoot.mWallpaperMayChange = false; 268 269 int i; 270 for (i = 0; i < appsCount; i++) { 271 final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); 272 // Clearing the mAnimatingExit flag before entering animation. It's set to true if app 273 // window is removed, or window relayout to invisible. This also affects window 274 // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the 275 // transition selection depends on wallpaper target visibility. 276 wtoken.clearAnimatingFlags(); 277 } 278 279 // Adjust wallpaper before we pull the lower/upper target, since pending changes 280 // (like the clearAnimatingFlags() above) might affect wallpaper target result. 281 // Or, the opening app window should be a wallpaper target. 282 mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent, 283 mService.mOpeningApps); 284 285 // Determine if closing and opening app token sets are wallpaper targets, in which case 286 // special animations are needed. 287 final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; 288 final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps) 289 && hasWallpaperTarget; 290 final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps) 291 && hasWallpaperTarget; 292 293 transit = maybeUpdateTransitToTranslucentAnim(transit); 294 transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, 295 closingAppHasWallpaper); 296 297 // Find the layout params of the top-most application window in the tokens, which is 298 // what will control the animation theme. If all closing windows are obscured, then there is 299 // no need to do an animation. This is the case, for example, when this transition is being 300 // done behind a dream window. 301 final ArraySet<Integer> activityTypes = collectActivityTypes(mService.mOpeningApps, 302 mService.mClosingApps); 303 final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw() 304 ? findAnimLayoutParamsToken(transit, activityTypes) 305 : null; 306 307 final LayoutParams animLp = getAnimLp(animLpToken); 308 overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes); 309 310 final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps) 311 || containsVoiceInteraction(mService.mOpeningApps); 312 313 final int layoutRedo; 314 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 315 try { 316 processApplicationsAnimatingInPlace(transit); 317 318 mTmpLayerAndToken.token = null; 319 handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); 320 final AppWindowToken topClosingApp = mTmpLayerAndToken.token; 321 final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, 322 voiceInteraction); 323 324 mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); 325 326 final int flags = mService.mAppTransition.getTransitFlags(); 327 layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, 328 topClosingApp, mService.mOpeningApps, mService.mClosingApps); 329 handleNonAppWindowsInTransition(transit, flags); 330 mService.mAppTransition.postAnimationCallback(); 331 mService.mAppTransition.clear(); 332 } finally { 333 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 334 } 335 336 mService.mTaskSnapshotController.onTransitionStarting(); 337 338 mService.mOpeningApps.clear(); 339 mService.mClosingApps.clear(); 340 mService.mUnknownAppVisibilityController.clear(); 341 342 // This has changed the visibility of windows, so perform 343 // a new layout to get them all up-to-date. 344 displayContent.setLayoutNeeded(); 345 346 // TODO(multidisplay): IMEs are only supported on the default display. 347 final DisplayContent dc = mService.getDefaultDisplayContentLocked(); 348 dc.computeImeTarget(true /* updateImeTarget */); 349 mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, 350 true /*updateInputWindows*/); 351 mService.mFocusMayChange = false; 352 353 mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, 354 mTempTransitionReasons.clone()).sendToTarget(); 355 356 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 357 358 return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; 359 } 360 getAnimLp(AppWindowToken wtoken)361 private static LayoutParams getAnimLp(AppWindowToken wtoken) { 362 final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null; 363 return mainWindow != null ? mainWindow.mAttrs : null; 364 } 365 366 /** 367 * Overrides the pending transition with the remote animation defined for the transition in the 368 * set of defined remote animations in the app window token. 369 */ overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, ArraySet<Integer> activityTypes)370 private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, 371 ArraySet<Integer> activityTypes) { 372 if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) { 373 // The crash transition has higher priority than any involved remote animations. 374 return; 375 } 376 if (animLpToken == null) { 377 return; 378 } 379 final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); 380 if (definition != null) { 381 final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); 382 if (adapter != null) { 383 mService.mAppTransition.overridePendingAppTransitionRemote(adapter); 384 } 385 } 386 } 387 388 /** 389 * @return The window token that determines the animation theme. 390 */ findAnimLayoutParamsToken(@ransitionType int transit, ArraySet<Integer> activityTypes)391 private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit, 392 ArraySet<Integer> activityTypes) { 393 AppWindowToken result; 394 395 // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. 396 result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, 397 w -> w.getRemoteAnimationDefinition() != null 398 && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); 399 if (result != null) { 400 return result; 401 } 402 result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, 403 w -> w.fillsParent() && w.findMainWindow() != null); 404 if (result != null) { 405 return result; 406 } 407 return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, 408 w -> w.findMainWindow() != null); 409 } 410 411 /** 412 * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in 413 * {@code array1} and {@code array2}. 414 */ collectActivityTypes(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2)415 private ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1, 416 ArraySet<AppWindowToken> array2) { 417 final ArraySet<Integer> result = new ArraySet<>(); 418 for (int i = array1.size() - 1; i >= 0; i--) { 419 result.add(array1.valueAt(i).getActivityType()); 420 } 421 for (int i = array2.size() - 1; i >= 0; i--) { 422 result.add(array2.valueAt(i).getActivityType()); 423 } 424 return result; 425 } 426 lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter)427 private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, 428 ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) { 429 final int array1count = array1.size(); 430 final int count = array1count + array2.size(); 431 int bestPrefixOrderIndex = Integer.MIN_VALUE; 432 AppWindowToken bestToken = null; 433 for (int i = 0; i < count; i++) { 434 final AppWindowToken wtoken = i < array1count 435 ? array1.valueAt(i) 436 : array2.valueAt(i - array1count); 437 final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); 438 if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { 439 bestPrefixOrderIndex = prefixOrderIndex; 440 bestToken = wtoken; 441 } 442 } 443 return bestToken; 444 } 445 containsVoiceInteraction(ArraySet<AppWindowToken> apps)446 private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) { 447 for (int i = apps.size() - 1; i >= 0; i--) { 448 if (apps.valueAt(i).mVoiceInteraction) { 449 return true; 450 } 451 } 452 return false; 453 } 454 handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction)455 private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp, 456 boolean voiceInteraction) { 457 AppWindowToken topOpeningApp = null; 458 int topOpeningLayer = Integer.MIN_VALUE; 459 final int appsCount = mService.mOpeningApps.size(); 460 for (int i = 0; i < appsCount; i++) { 461 AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); 462 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); 463 464 if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) { 465 // This token isn't going to be animating. Add it to the list of tokens to 466 // be notified of app transition complete since the notification will not be 467 // sent be the app window animator. 468 mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); 469 } 470 wtoken.updateReportedVisibilityLocked(); 471 wtoken.waitingToShow = false; 472 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 473 ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()"); 474 mService.openSurfaceTransaction(); 475 try { 476 wtoken.showAllWindowsLocked(); 477 } finally { 478 mService.closeSurfaceTransaction("handleAppTransitionReadyLocked"); 479 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 480 "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()"); 481 } 482 483 if (animLp != null) { 484 final int layer = wtoken.getHighestAnimLayer(); 485 if (topOpeningApp == null || layer > topOpeningLayer) { 486 topOpeningApp = wtoken; 487 topOpeningLayer = layer; 488 } 489 } 490 if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) { 491 wtoken.attachThumbnailAnimation(); 492 } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { 493 wtoken.attachCrossProfileAppsThumbnailAnimation(); 494 } 495 } 496 return topOpeningApp; 497 } 498 handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction, LayerAndToken layerAndToken)499 private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction, 500 LayerAndToken layerAndToken) { 501 final int appsCount; 502 appsCount = mService.mClosingApps.size(); 503 for (int i = 0; i < appsCount; i++) { 504 AppWindowToken wtoken = mService.mClosingApps.valueAt(i); 505 506 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); 507 // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not 508 // animating? 509 wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); 510 wtoken.updateReportedVisibilityLocked(); 511 // Force the allDrawn flag, because we want to start 512 // this guy's animations regardless of whether it's 513 // gotten drawn. 514 wtoken.allDrawn = true; 515 wtoken.deferClearAllDrawn = false; 516 // Ensure that apps that are mid-starting are also scheduled to have their 517 // starting windows removed after the animation is complete 518 if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit 519 && wtoken.getController() != null) { 520 wtoken.getController().removeStartingWindow(); 521 } 522 523 if (animLp != null) { 524 int layer = wtoken.getHighestAnimLayer(); 525 if (layerAndToken.token == null || layer > layerAndToken.layer) { 526 layerAndToken.token = wtoken; 527 layerAndToken.layer = layer; 528 } 529 } 530 if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) { 531 wtoken.attachThumbnailAnimation(); 532 } 533 } 534 } 535 handleNonAppWindowsInTransition(int transit, int flags)536 private void handleNonAppWindowsInTransition(int transit, int flags) { 537 if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { 538 if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 539 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) { 540 Animation anim = mService.mPolicy.createKeyguardWallpaperExit( 541 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 542 if (anim != null) { 543 mService.getDefaultDisplayContentLocked().mWallpaperController 544 .startWallpaperAnimation(anim); 545 } 546 } 547 } 548 if (transit == TRANSIT_KEYGUARD_GOING_AWAY 549 || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { 550 mService.getDefaultDisplayContentLocked().startKeyguardExitOnNonAppWindows( 551 transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, 552 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 553 } 554 } 555 transitionGoodToGo(int appsCount, SparseIntArray outReasons)556 private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) { 557 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 558 "Checking " + appsCount + " opening apps (frozen=" 559 + mService.mDisplayFrozen + " timeout=" 560 + mService.mAppTransition.isTimeout() + ")..."); 561 final ScreenRotationAnimation screenRotationAnimation = 562 mService.mAnimator.getScreenRotationAnimationLocked( 563 Display.DEFAULT_DISPLAY); 564 565 outReasons.clear(); 566 if (!mService.mAppTransition.isTimeout()) { 567 // Imagine the case where we are changing orientation due to an app transition, but a previous 568 // orientation change is still in progress. We won't process the orientation change 569 // for our transition because we need to wait for the rotation animation to finish. 570 // If we start the app transition at this point, we will interrupt it halfway with a new rotation 571 // animation after the old one finally finishes. It's better to defer the 572 // app transition. 573 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() && 574 mService.rotationNeedsUpdateLocked()) { 575 if (DEBUG_APP_TRANSITIONS) { 576 Slog.v(TAG, "Delaying app transition for screen rotation animation to finish"); 577 } 578 return false; 579 } 580 for (int i = 0; i < appsCount; i++) { 581 AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); 582 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 583 "Check opening app=" + wtoken + ": allDrawn=" 584 + wtoken.allDrawn + " startingDisplayed=" 585 + wtoken.startingDisplayed + " startingMoved=" 586 + wtoken.startingMoved + " isRelaunching()=" 587 + wtoken.isRelaunching() + " startingWindow=" 588 + wtoken.startingWindow); 589 590 591 final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); 592 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { 593 return false; 594 } 595 final int windowingMode = wtoken.getWindowingMode(); 596 if (allDrawn) { 597 outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); 598 } else { 599 outReasons.put(windowingMode, 600 wtoken.startingData instanceof SplashScreenStartingData 601 ? APP_TRANSITION_SPLASH_SCREEN 602 : APP_TRANSITION_SNAPSHOT); 603 } 604 } 605 606 // We also need to wait for the specs to be fetched, if needed. 607 if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) { 608 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true"); 609 return false; 610 } 611 612 if (!mService.mUnknownAppVisibilityController.allResolved()) { 613 if (DEBUG_APP_TRANSITIONS) { 614 Slog.v(TAG, "unknownApps is not empty: " 615 + mService.mUnknownAppVisibilityController.getDebugMessage()); 616 } 617 return false; 618 } 619 620 // If the wallpaper is visible, we need to check it's ready too. 621 boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || 622 mWallpaperControllerLocked.wallpaperTransitionReady(); 623 if (wallpaperReady) { 624 return true; 625 } 626 return false; 627 } 628 return true; 629 } 630 maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, boolean closingAppHasWallpaper)631 private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, 632 boolean closingAppHasWallpaper) { 633 // Given no app transition pass it through instead of a wallpaper transition. 634 // Never convert the crashing transition. 635 // Never update the transition for the wallpaper if we are just docking from recents 636 if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE 637 || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) { 638 return transit; 639 } 640 641 // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper 642 final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); 643 final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating() 644 ? null : wallpaperTarget; 645 final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps; 646 final ArraySet<AppWindowToken> closingApps = mService.mClosingApps; 647 final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps, 648 false /* ignoreHidden */); 649 final AppWindowToken topClosingApp = getTopApp(mService.mClosingApps, 650 true /* ignoreHidden */); 651 652 boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); 653 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 654 "New wallpaper target=" + wallpaperTarget 655 + ", oldWallpaper=" + oldWallpaper 656 + ", openingApps=" + openingApps 657 + ", closingApps=" + closingApps); 658 659 if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { 660 transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 661 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 662 "New transit: " + AppTransition.appTransitionToString(transit)); 663 } 664 // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic 665 // relies on the fact that we always execute a Keyguard transition after preparing one. 666 else if (!isKeyguardGoingAwayTransit(transit)) { 667 if (closingAppHasWallpaper && openingAppHasWallpaper) { 668 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); 669 switch (transit) { 670 case TRANSIT_ACTIVITY_OPEN: 671 case TRANSIT_TASK_OPEN: 672 case TRANSIT_TASK_TO_FRONT: 673 transit = TRANSIT_WALLPAPER_INTRA_OPEN; 674 break; 675 case TRANSIT_ACTIVITY_CLOSE: 676 case TRANSIT_TASK_CLOSE: 677 case TRANSIT_TASK_TO_BACK: 678 transit = TRANSIT_WALLPAPER_INTRA_CLOSE; 679 break; 680 } 681 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, 682 "New transit: " + AppTransition.appTransitionToString(transit)); 683 } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty() 684 && !openingApps.contains(oldWallpaper.mAppToken) 685 && closingApps.contains(oldWallpaper.mAppToken) 686 && topClosingApp == oldWallpaper.mAppToken) { 687 // We are transitioning from an activity with a wallpaper to one without. 688 transit = TRANSIT_WALLPAPER_CLOSE; 689 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " 690 + AppTransition.appTransitionToString(transit)); 691 } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() 692 && openingApps.contains(wallpaperTarget.mAppToken) 693 && topOpeningApp == wallpaperTarget.mAppToken 694 && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) { 695 // We are transitioning from an activity without 696 // a wallpaper to now showing the wallpaper 697 transit = TRANSIT_WALLPAPER_OPEN; 698 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " 699 + AppTransition.appTransitionToString(transit)); 700 } 701 } 702 return transit; 703 } 704 705 /** 706 * There are cases where we open/close a new task/activity, but in reality only a translucent 707 * activity on top of existing activities is opening/closing. For that one, we have a different 708 * animation because non of the task/activity animations actually work well with translucent 709 * apps. 710 * 711 * @param transit The current transition type. 712 * @return The current transition type or 713 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/ 714 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the 715 * situation. 716 */ 717 @VisibleForTesting maybeUpdateTransitToTranslucentAnim(int transit)718 int maybeUpdateTransitToTranslucentAnim(int transit) { 719 final boolean taskOrActivity = AppTransition.isTaskTransit(transit) 720 || AppTransition.isActivityTransit(transit); 721 boolean allOpeningVisible = true; 722 boolean allTranslucentOpeningApps = !mService.mOpeningApps.isEmpty(); 723 for (int i = mService.mOpeningApps.size() - 1; i >= 0; i--) { 724 final AppWindowToken token = mService.mOpeningApps.valueAt(i); 725 if (!token.isVisible()) { 726 allOpeningVisible = false; 727 if (token.fillsParent()) { 728 allTranslucentOpeningApps = false; 729 } 730 } 731 } 732 boolean allTranslucentClosingApps = !mService.mClosingApps.isEmpty(); 733 for (int i = mService.mClosingApps.size() - 1; i >= 0; i--) { 734 if (mService.mClosingApps.valueAt(i).fillsParent()) { 735 allTranslucentClosingApps = false; 736 break; 737 } 738 } 739 740 if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) { 741 return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; 742 } 743 if (taskOrActivity && allTranslucentOpeningApps && mService.mClosingApps.isEmpty()) { 744 return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; 745 } 746 return transit; 747 } 748 canBeWallpaperTarget(ArraySet<AppWindowToken> apps)749 private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) { 750 for (int i = apps.size() - 1; i >= 0; i--) { 751 if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { 752 return true; 753 } 754 } 755 return false; 756 } 757 758 /** 759 * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to 760 * compare z-order. 761 * 762 * @param apps The list of apps to search. 763 * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}. 764 * @return The top {@link AppWindowToken}. 765 */ getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden)766 private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) { 767 int topPrefixOrderIndex = Integer.MIN_VALUE; 768 AppWindowToken topApp = null; 769 for (int i = apps.size() - 1; i >= 0; i--) { 770 final AppWindowToken app = apps.valueAt(i); 771 if (ignoreHidden && app.isHidden()) { 772 continue; 773 } 774 final int prefixOrderIndex = app.getPrefixOrderIndex(); 775 if (prefixOrderIndex > topPrefixOrderIndex) { 776 topPrefixOrderIndex = prefixOrderIndex; 777 topApp = app; 778 } 779 } 780 return topApp; 781 } 782 processApplicationsAnimatingInPlace(int transit)783 private void processApplicationsAnimatingInPlace(int transit) { 784 if (transit == TRANSIT_TASK_IN_PLACE) { 785 // Find the focused window 786 final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow(); 787 if (win != null) { 788 final AppWindowToken wtoken = win.mAppToken; 789 if (DEBUG_APP_TRANSITIONS) 790 Slog.v(TAG, "Now animating app in place " + wtoken); 791 wtoken.cancelAnimation(); 792 wtoken.applyAnimationLocked(null, transit, false, false); 793 wtoken.updateReportedVisibilityLocked(); 794 wtoken.showAllWindowsLocked(); 795 } 796 } 797 } 798 requestTraversal()799 void requestTraversal() { 800 if (!mTraversalScheduled) { 801 mTraversalScheduled = true; 802 mService.mAnimationHandler.post(mPerformSurfacePlacement); 803 } 804 } 805 dump(PrintWriter pw, String prefix)806 public void dump(PrintWriter pw, String prefix) { 807 pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled); 808 pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow); 809 pw.println(prefix + "mObscuringWindow=" + mService.mRoot.mObscuringWindow); 810 } 811 } 812