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