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