1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 21 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 22 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 23 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 24 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 25 26 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 27 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; 29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; 30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; 31 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 33 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT; 34 35 import android.graphics.Bitmap; 36 import android.graphics.Rect; 37 import android.os.Bundle; 38 import android.os.Debug; 39 import android.os.IBinder; 40 import android.os.RemoteException; 41 import android.os.SystemClock; 42 import android.util.ArraySet; 43 import android.util.Slog; 44 import android.view.DisplayInfo; 45 import android.view.SurfaceControl; 46 import android.view.WindowManager; 47 import android.view.animation.Animation; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.util.ToBooleanFunction; 51 52 import java.io.PrintWriter; 53 import java.util.ArrayList; 54 55 /** 56 * Controls wallpaper windows visibility, ordering, and so on. 57 * NOTE: All methods in this class must be called with the window manager service lock held. 58 */ 59 class WallpaperController { 60 private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM; 61 private WindowManagerService mService; 62 private final DisplayContent mDisplayContent; 63 64 private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>(); 65 66 // If non-null, this is the currently visible window that is associated 67 // with the wallpaper. 68 private WindowState mWallpaperTarget = null; 69 // If non-null, we are in the middle of animating from one wallpaper target 70 // to another, and this is the previous wallpaper target. 71 private WindowState mPrevWallpaperTarget = null; 72 73 private float mLastWallpaperX = -1; 74 private float mLastWallpaperY = -1; 75 private float mLastWallpaperXStep = -1; 76 private float mLastWallpaperYStep = -1; 77 private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE; 78 private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE; 79 80 // This is set when we are waiting for a wallpaper to tell us it is done 81 // changing its scroll position. 82 private WindowState mWaitingOnWallpaper; 83 84 // The last time we had a timeout when waiting for a wallpaper. 85 private long mLastWallpaperTimeoutTime; 86 // We give a wallpaper up to 150ms to finish scrolling. 87 private static final long WALLPAPER_TIMEOUT = 150; 88 // Time we wait after a timeout before trying to wait again. 89 private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; 90 91 // Set to the wallpaper window we would like to hide once the transition animations are done. 92 // This is useful in cases where we don't want the wallpaper to be hidden when the close app 93 // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper 94 // target and isn't done animating in. 95 WindowState mDeferredHideWallpaper = null; 96 97 // We give a wallpaper up to 500ms to finish drawing before playing app transitions. 98 private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500; 99 private static final int WALLPAPER_DRAW_NORMAL = 0; 100 private static final int WALLPAPER_DRAW_PENDING = 1; 101 private static final int WALLPAPER_DRAW_TIMEOUT = 2; 102 private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; 103 104 /** 105 * Temporary storage for taking a screenshot of the wallpaper. 106 * @see #screenshotWallpaperLocked() 107 */ 108 private WindowState mTmpTopWallpaper; 109 110 private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); 111 112 private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { 113 final WindowAnimator winAnimator = mService.mAnimator; 114 if ((w.mAttrs.type == TYPE_WALLPAPER)) { 115 if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) { 116 mFindResults.setTopWallpaper(w); 117 mFindResults.resetTopWallpaper = false; 118 } 119 return false; 120 } 121 122 mFindResults.resetTopWallpaper = true; 123 if (w.mAppToken != null && w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) { 124 125 // If this window's app token is hidden and not animating, it is of no interest to us. 126 if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w); 127 return false; 128 } 129 if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen() 130 + " mDrawState=" + w.mWinAnimator.mDrawState); 131 132 if (w.mWillReplaceWindow && mWallpaperTarget == null 133 && !mFindResults.useTopWallpaperAsTarget) { 134 // When we are replacing a window and there was wallpaper before replacement, we want to 135 // keep the window until the new windows fully appear and can determine the visibility, 136 // to avoid flickering. 137 mFindResults.setUseTopWallpaperAsTarget(true); 138 } 139 140 final boolean keyguardGoingAwayWithWallpaper = (w.mAppToken != null 141 && w.mAppToken.isSelfAnimating() 142 && AppTransition.isKeyguardGoingAwayTransit(w.mAppToken.getTransit()) 143 && (w.mAppToken.getTransitFlags() 144 & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); 145 146 boolean needsShowWhenLockedWallpaper = false; 147 if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 148 && mService.mPolicy.isKeyguardLocked() 149 && mService.mPolicy.isKeyguardOccluded()) { 150 // The lowest show when locked window decides whether we need to put the wallpaper 151 // behind. 152 needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) 153 || (w.mAppToken != null && !w.mAppToken.fillsParent()); 154 } 155 156 if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) { 157 // Keep the wallpaper during Keyguard exit but also when it's needed for a 158 // non-fullscreen show when locked activity. 159 mFindResults.setUseTopWallpaperAsTarget(true); 160 } 161 162 final RecentsAnimationController recentsAnimationController = 163 mService.getRecentsAnimationController(); 164 final boolean animationWallpaper = w.mAppToken != null && w.mAppToken.getAnimation() != null 165 && w.mAppToken.getAnimation().getShowWallpaper(); 166 final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 167 || animationWallpaper; 168 final boolean isRecentsTransitionTarget = (recentsAnimationController != null 169 && recentsAnimationController.isWallpaperVisible(w)); 170 if (isRecentsTransitionTarget) { 171 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); 172 mFindResults.setWallpaperTarget(w); 173 return true; 174 } else if (hasWallpaper && w.isOnScreen() 175 && (mWallpaperTarget == w || w.isDrawFinishedLw())) { 176 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); 177 mFindResults.setWallpaperTarget(w); 178 if (w == mWallpaperTarget && w.isAnimating()) { 179 // The current wallpaper target is animating, so we'll look behind it for 180 // another possible target and figure out what is going on later. 181 if (DEBUG_WALLPAPER) Slog.v(TAG, 182 "Win " + w + ": token animating, looking behind."); 183 } 184 // Found a target! End search. 185 return true; 186 } 187 return false; 188 }; 189 WallpaperController(WindowManagerService service, DisplayContent displayContent)190 WallpaperController(WindowManagerService service, DisplayContent displayContent) { 191 mService = service; 192 mDisplayContent = displayContent; 193 } 194 getWallpaperTarget()195 WindowState getWallpaperTarget() { 196 return mWallpaperTarget; 197 } 198 isWallpaperTarget(WindowState win)199 boolean isWallpaperTarget(WindowState win) { 200 return win == mWallpaperTarget; 201 } 202 isBelowWallpaperTarget(WindowState win)203 boolean isBelowWallpaperTarget(WindowState win) { 204 return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer; 205 } 206 isWallpaperVisible()207 boolean isWallpaperVisible() { 208 return isWallpaperVisible(mWallpaperTarget); 209 } 210 211 /** 212 * Starts {@param a} on all wallpaper windows. 213 */ startWallpaperAnimation(Animation a)214 void startWallpaperAnimation(Animation a) { 215 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 216 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 217 token.startAnimation(a); 218 } 219 } 220 isWallpaperVisible(WindowState wallpaperTarget)221 private final boolean isWallpaperVisible(WindowState wallpaperTarget) { 222 final RecentsAnimationController recentsAnimationController = 223 mService.getRecentsAnimationController(); 224 boolean isAnimatingWithRecentsComponent = recentsAnimationController != null 225 && recentsAnimationController.isWallpaperVisible(wallpaperTarget); 226 if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" 227 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") 228 + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null) 229 ? wallpaperTarget.mAppToken.isSelfAnimating() : null) 230 + " prev=" + mPrevWallpaperTarget 231 + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent); 232 return (wallpaperTarget != null 233 && (!wallpaperTarget.mObscured 234 || isAnimatingWithRecentsComponent 235 || (wallpaperTarget.mAppToken != null 236 && wallpaperTarget.mAppToken.isSelfAnimating()))) 237 || mPrevWallpaperTarget != null; 238 } 239 isWallpaperTargetAnimating()240 boolean isWallpaperTargetAnimating() { 241 return mWallpaperTarget != null && mWallpaperTarget.isAnimating() 242 && (mWallpaperTarget.mAppToken == null 243 || !mWallpaperTarget.mAppToken.isWaitingForTransitionStart()); 244 } 245 updateWallpaperVisibility()246 void updateWallpaperVisibility() { 247 final boolean visible = isWallpaperVisible(mWallpaperTarget); 248 249 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 250 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 251 token.updateWallpaperVisibility(visible); 252 } 253 } 254 hideDeferredWallpapersIfNeeded()255 void hideDeferredWallpapersIfNeeded() { 256 if (mDeferredHideWallpaper != null) { 257 hideWallpapers(mDeferredHideWallpaper); 258 mDeferredHideWallpaper = null; 259 } 260 } 261 hideWallpapers(final WindowState winGoingAway)262 void hideWallpapers(final WindowState winGoingAway) { 263 if (mWallpaperTarget != null 264 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) { 265 return; 266 } 267 if (mWallpaperTarget != null 268 && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) { 269 // Defer hiding the wallpaper when app transition is running until the animations 270 // are done. 271 mDeferredHideWallpaper = winGoingAway; 272 return; 273 } 274 275 final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway); 276 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 277 final WallpaperWindowToken token = mWallpaperTokens.get(i); 278 token.hideWallpaperToken(wasDeferred, "hideWallpapers"); 279 if (DEBUG_WALLPAPER_LIGHT && !token.isHidden()) Slog.d(TAG, "Hiding wallpaper " + token 280 + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev=" 281 + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " ")); 282 } 283 } 284 updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync)285 boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) { 286 int xOffset = 0; 287 int yOffset = 0; 288 boolean rawChanged = false; 289 // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to 290 // match the behavior of most Launchers 291 float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f; 292 float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX; 293 float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; 294 int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw; 295 int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0; 296 if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 297 offset += mLastWallpaperDisplayOffsetX; 298 } 299 xOffset = offset; 300 301 if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { 302 wallpaperWin.mWallpaperX = wpx; 303 wallpaperWin.mWallpaperXStep = wpxs; 304 rawChanged = true; 305 } 306 307 float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; 308 float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; 309 int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh; 310 offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0; 311 if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 312 offset += mLastWallpaperDisplayOffsetY; 313 } 314 yOffset = offset; 315 316 if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { 317 wallpaperWin.mWallpaperY = wpy; 318 wallpaperWin.mWallpaperYStep = wpys; 319 rawChanged = true; 320 } 321 322 boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset); 323 324 if (rawChanged && (wallpaperWin.mAttrs.privateFlags & 325 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { 326 try { 327 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " 328 + wallpaperWin + " x=" + wallpaperWin.mWallpaperX 329 + " y=" + wallpaperWin.mWallpaperY); 330 if (sync) { 331 mWaitingOnWallpaper = wallpaperWin; 332 } 333 wallpaperWin.mClient.dispatchWallpaperOffsets( 334 wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, 335 wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync); 336 if (sync) { 337 if (mWaitingOnWallpaper != null) { 338 long start = SystemClock.uptimeMillis(); 339 if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY) 340 < start) { 341 try { 342 if (DEBUG_WALLPAPER) Slog.v(TAG, 343 "Waiting for offset complete..."); 344 mService.mGlobalLock.wait(WALLPAPER_TIMEOUT); 345 } catch (InterruptedException e) { 346 } 347 if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!"); 348 if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) { 349 Slog.i(TAG, "Timeout waiting for wallpaper to offset: " 350 + wallpaperWin); 351 mLastWallpaperTimeoutTime = start; 352 } 353 } 354 mWaitingOnWallpaper = null; 355 } 356 } 357 } catch (RemoteException e) { 358 } 359 } 360 361 return changed; 362 } 363 setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)364 void setWindowWallpaperPosition( 365 WindowState window, float x, float y, float xStep, float yStep) { 366 if (window.mWallpaperX != x || window.mWallpaperY != y) { 367 window.mWallpaperX = x; 368 window.mWallpaperY = y; 369 window.mWallpaperXStep = xStep; 370 window.mWallpaperYStep = yStep; 371 updateWallpaperOffsetLocked(window, true); 372 } 373 } 374 setWindowWallpaperDisplayOffset(WindowState window, int x, int y)375 void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) { 376 if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) { 377 window.mWallpaperDisplayOffsetX = x; 378 window.mWallpaperDisplayOffsetY = y; 379 updateWallpaperOffsetLocked(window, true); 380 } 381 } 382 sendWindowWallpaperCommand( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)383 Bundle sendWindowWallpaperCommand( 384 WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) { 385 if (window == mWallpaperTarget || window == mPrevWallpaperTarget) { 386 boolean doWait = sync; 387 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 388 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 389 token.sendWindowWallpaperCommand(action, x, y, z, extras, sync); 390 } 391 392 if (doWait) { 393 // TODO: Need to wait for result. 394 } 395 } 396 397 return null; 398 } 399 updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)400 private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { 401 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 402 final int dw = displayInfo.logicalWidth; 403 final int dh = displayInfo.logicalHeight; 404 405 WindowState target = mWallpaperTarget; 406 if (target != null) { 407 if (target.mWallpaperX >= 0) { 408 mLastWallpaperX = target.mWallpaperX; 409 } else if (changingTarget.mWallpaperX >= 0) { 410 mLastWallpaperX = changingTarget.mWallpaperX; 411 } 412 if (target.mWallpaperY >= 0) { 413 mLastWallpaperY = target.mWallpaperY; 414 } else if (changingTarget.mWallpaperY >= 0) { 415 mLastWallpaperY = changingTarget.mWallpaperY; 416 } 417 if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 418 mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX; 419 } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 420 mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX; 421 } 422 if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 423 mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY; 424 } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 425 mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY; 426 } 427 if (target.mWallpaperXStep >= 0) { 428 mLastWallpaperXStep = target.mWallpaperXStep; 429 } else if (changingTarget.mWallpaperXStep >= 0) { 430 mLastWallpaperXStep = changingTarget.mWallpaperXStep; 431 } 432 if (target.mWallpaperYStep >= 0) { 433 mLastWallpaperYStep = target.mWallpaperYStep; 434 } else if (changingTarget.mWallpaperYStep >= 0) { 435 mLastWallpaperYStep = changingTarget.mWallpaperYStep; 436 } 437 } 438 439 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 440 mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(dw, dh, sync); 441 } 442 } 443 clearLastWallpaperTimeoutTime()444 void clearLastWallpaperTimeoutTime() { 445 mLastWallpaperTimeoutTime = 0; 446 } 447 wallpaperCommandComplete(IBinder window)448 void wallpaperCommandComplete(IBinder window) { 449 if (mWaitingOnWallpaper != null && 450 mWaitingOnWallpaper.mClient.asBinder() == window) { 451 mWaitingOnWallpaper = null; 452 mService.mGlobalLock.notifyAll(); 453 } 454 } 455 wallpaperOffsetsComplete(IBinder window)456 void wallpaperOffsetsComplete(IBinder window) { 457 if (mWaitingOnWallpaper != null && 458 mWaitingOnWallpaper.mClient.asBinder() == window) { 459 mWaitingOnWallpaper = null; 460 mService.mGlobalLock.notifyAll(); 461 } 462 } 463 findWallpaperTarget()464 private void findWallpaperTarget() { 465 mFindResults.reset(); 466 if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) { 467 // In freeform mode we set the wallpaper as its own target, so we don't need an 468 // additional window to make it visible. 469 mFindResults.setUseTopWallpaperAsTarget(true); 470 } 471 472 mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); 473 474 if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { 475 mFindResults.setWallpaperTarget(mFindResults.topWallpaper); 476 } 477 } 478 isFullscreen(WindowManager.LayoutParams attrs)479 private boolean isFullscreen(WindowManager.LayoutParams attrs) { 480 return attrs.x == 0 && attrs.y == 0 481 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT; 482 } 483 484 /** Updates the target wallpaper if needed and returns true if an update happened. */ updateWallpaperWindowsTarget(FindWallpaperTargetResult result)485 private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) { 486 487 WindowState wallpaperTarget = result.wallpaperTarget; 488 489 if (mWallpaperTarget == wallpaperTarget 490 || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) { 491 492 if (mPrevWallpaperTarget == null) { 493 return; 494 } 495 496 // Is it time to stop animating? 497 if (!mPrevWallpaperTarget.isAnimatingLw()) { 498 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!"); 499 mPrevWallpaperTarget = null; 500 mWallpaperTarget = wallpaperTarget; 501 } 502 return; 503 } 504 505 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, 506 "New wallpaper target: " + wallpaperTarget + " prevTarget: " + mWallpaperTarget); 507 508 mPrevWallpaperTarget = null; 509 510 final WindowState prevWallpaperTarget = mWallpaperTarget; 511 mWallpaperTarget = wallpaperTarget; 512 513 if (wallpaperTarget == null || prevWallpaperTarget == null) { 514 return; 515 } 516 517 // Now what is happening... if the current and new targets are animating, 518 // then we are in our super special mode! 519 boolean oldAnim = prevWallpaperTarget.isAnimatingLw(); 520 boolean foundAnim = wallpaperTarget.isAnimatingLw(); 521 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, 522 "New animation: " + foundAnim + " old animation: " + oldAnim); 523 524 if (!foundAnim || !oldAnim) { 525 return; 526 } 527 528 if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) { 529 return; 530 } 531 532 final boolean newTargetHidden = wallpaperTarget.mAppToken != null 533 && wallpaperTarget.mAppToken.hiddenRequested; 534 final boolean oldTargetHidden = prevWallpaperTarget.mAppToken != null 535 && prevWallpaperTarget.mAppToken.hiddenRequested; 536 537 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: " 538 + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget 539 + " hidden=" + newTargetHidden); 540 541 mPrevWallpaperTarget = prevWallpaperTarget; 542 543 if (newTargetHidden && !oldTargetHidden) { 544 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target."); 545 // Use the old target if new target is hidden but old target 546 // is not. If they're both hidden, still use the new target. 547 mWallpaperTarget = prevWallpaperTarget; 548 } else if (newTargetHidden == oldTargetHidden 549 && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken) 550 && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken) 551 || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) { 552 // If they're both hidden (or both not hidden), prefer the one that's currently in 553 // opening or closing app list, this allows transition selection logic to better 554 // determine the wallpaper status of opening/closing apps. 555 mWallpaperTarget = prevWallpaperTarget; 556 } 557 558 result.setWallpaperTarget(wallpaperTarget); 559 } 560 updateWallpaperTokens(boolean visible)561 private void updateWallpaperTokens(boolean visible) { 562 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 563 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 564 token.updateWallpaperWindows(visible); 565 token.getDisplayContent().assignWindowLayers(false); 566 } 567 } 568 adjustWallpaperWindows()569 void adjustWallpaperWindows() { 570 mDisplayContent.mWallpaperMayChange = false; 571 572 // First find top-most window that has asked to be on top of the wallpaper; 573 // all wallpapers go behind it. 574 findWallpaperTarget(); 575 updateWallpaperWindowsTarget(mFindResults); 576 577 // The window is visible to the compositor...but is it visible to the user? 578 // That is what the wallpaper cares about. 579 final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget); 580 if (DEBUG_WALLPAPER) { 581 Slog.v(TAG, "Wallpaper visibility: " + visible + " at display " 582 + mDisplayContent.getDisplayId()); 583 } 584 585 if (visible) { 586 if (mWallpaperTarget.mWallpaperX >= 0) { 587 mLastWallpaperX = mWallpaperTarget.mWallpaperX; 588 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; 589 } 590 if (mWallpaperTarget.mWallpaperY >= 0) { 591 mLastWallpaperY = mWallpaperTarget.mWallpaperY; 592 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; 593 } 594 if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 595 mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; 596 } 597 if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 598 mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; 599 } 600 } 601 602 updateWallpaperTokens(visible); 603 604 if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget 605 + " prev=" + mPrevWallpaperTarget); 606 } 607 processWallpaperDrawPendingTimeout()608 boolean processWallpaperDrawPendingTimeout() { 609 if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) { 610 mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT; 611 if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, 612 "*** WALLPAPER DRAW TIMEOUT"); 613 614 // If there was a pending recents animation, start the animation anyways (it's better 615 // to not see the wallpaper than for the animation to not start) 616 if (mService.getRecentsAnimationController() != null) { 617 mService.getRecentsAnimationController().startAnimation(); 618 } 619 return true; 620 } 621 return false; 622 } 623 wallpaperTransitionReady()624 boolean wallpaperTransitionReady() { 625 boolean transitionReady = true; 626 boolean wallpaperReady = true; 627 for (int curTokenIndex = mWallpaperTokens.size() - 1; 628 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) { 629 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex); 630 if (token.hasVisibleNotDrawnWallpaper()) { 631 // We've told this wallpaper to be visible, but it is not drawn yet 632 wallpaperReady = false; 633 if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { 634 // wait for this wallpaper until it is drawn or timeout 635 transitionReady = false; 636 } 637 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { 638 mWallpaperDrawState = WALLPAPER_DRAW_PENDING; 639 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 640 mService.mH.sendMessageDelayed( 641 mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this), 642 WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); 643 644 } 645 if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, 646 "Wallpaper should be visible but has not been drawn yet. " + 647 "mWallpaperDrawState=" + mWallpaperDrawState); 648 break; 649 } 650 } 651 if (wallpaperReady) { 652 mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; 653 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 654 } 655 656 return transitionReady; 657 } 658 659 /** 660 * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of 661 * the opening apps should be a wallpaper target. 662 */ adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> changingApps)663 void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps, 664 ArraySet<AppWindowToken> changingApps) { 665 boolean adjust = false; 666 if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { 667 adjust = true; 668 } else { 669 for (int i = openingApps.size() - 1; i >= 0; --i) { 670 final AppWindowToken token = openingApps.valueAt(i); 671 if (token.windowsCanBeWallpaperTarget()) { 672 adjust = true; 673 break; 674 } 675 } 676 if (!adjust) { 677 for (int i = changingApps.size() - 1; i >= 0; --i) { 678 final AppWindowToken token = changingApps.valueAt(i); 679 if (token.windowsCanBeWallpaperTarget()) { 680 adjust = true; 681 break; 682 } 683 } 684 } 685 } 686 687 if (adjust) { 688 adjustWallpaperWindows(); 689 } 690 } 691 addWallpaperToken(WallpaperWindowToken token)692 void addWallpaperToken(WallpaperWindowToken token) { 693 mWallpaperTokens.add(token); 694 } 695 removeWallpaperToken(WallpaperWindowToken token)696 void removeWallpaperToken(WallpaperWindowToken token) { 697 mWallpaperTokens.remove(token); 698 } 699 700 701 @VisibleForTesting canScreenshotWallpaper()702 boolean canScreenshotWallpaper() { 703 return canScreenshotWallpaper(getTopVisibleWallpaper()); 704 } 705 canScreenshotWallpaper(WindowState wallpaperWindowState)706 private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) { 707 if (!mService.mPolicy.isScreenOn()) { 708 if (DEBUG_SCREENSHOT) { 709 Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); 710 } 711 return false; 712 } 713 714 if (wallpaperWindowState == null) { 715 if (DEBUG_SCREENSHOT) { 716 Slog.i(TAG_WM, "No visible wallpaper to screenshot"); 717 } 718 return false; 719 } 720 return true; 721 } 722 723 /** 724 * Take a screenshot of the wallpaper if it's visible. 725 * 726 * @return Bitmap of the wallpaper 727 */ screenshotWallpaperLocked()728 Bitmap screenshotWallpaperLocked() { 729 final WindowState wallpaperWindowState = getTopVisibleWallpaper(); 730 if (!canScreenshotWallpaper(wallpaperWindowState)) { 731 return null; 732 } 733 734 final Rect bounds = wallpaperWindowState.getBounds(); 735 bounds.offsetTo(0, 0); 736 737 SurfaceControl.ScreenshotGraphicBuffer wallpaperBuffer = SurfaceControl.captureLayers( 738 wallpaperWindowState.getSurfaceControl().getHandle(), bounds, 1 /* frameScale */); 739 740 if (wallpaperBuffer == null) { 741 Slog.w(TAG_WM, "Failed to screenshot wallpaper"); 742 return null; 743 } 744 return Bitmap.wrapHardwareBuffer( 745 wallpaperBuffer.getGraphicBuffer(), wallpaperBuffer.getColorSpace()); 746 } 747 getTopVisibleWallpaper()748 private WindowState getTopVisibleWallpaper() { 749 mTmpTopWallpaper = null; 750 751 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 752 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 753 token.forAllWindows(w -> { 754 final WindowStateAnimator winAnim = w.mWinAnimator; 755 if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) { 756 mTmpTopWallpaper = w; 757 return true; 758 } 759 return false; 760 }, true /* traverseTopToBottom */); 761 } 762 763 return mTmpTopWallpaper; 764 } 765 dump(PrintWriter pw, String prefix)766 void dump(PrintWriter pw, String prefix) { 767 pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId()); 768 pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget); 769 if (mPrevWallpaperTarget != null) { 770 pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget); 771 } 772 pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX); 773 pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY); 774 if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE 775 || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 776 pw.print(prefix); 777 pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX); 778 pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY); 779 } 780 } 781 782 /** Helper class for storing the results of a wallpaper target find operation. */ 783 final private static class FindWallpaperTargetResult { 784 WindowState topWallpaper = null; 785 boolean useTopWallpaperAsTarget = false; 786 WindowState wallpaperTarget = null; 787 boolean resetTopWallpaper = false; 788 setTopWallpaper(WindowState win)789 void setTopWallpaper(WindowState win) { 790 topWallpaper = win; 791 } 792 setWallpaperTarget(WindowState win)793 void setWallpaperTarget(WindowState win) { 794 wallpaperTarget = win; 795 } 796 setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget)797 void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) { 798 useTopWallpaperAsTarget = topWallpaperAsTarget; 799 } 800 reset()801 void reset() { 802 topWallpaper = null; 803 wallpaperTarget = null; 804 useTopWallpaperAsTarget = false; 805 resetTopWallpaper = false; 806 } 807 } 808 } 809