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