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