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.Display.DEFAULT_DISPLAY; 23 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 24 25 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WALLPAPER; 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.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 import static com.android.window.flags.Flags.multiCrop; 35 36 import android.annotation.Nullable; 37 import android.content.res.Resources; 38 import android.graphics.Bitmap; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.os.Bundle; 42 import android.os.Debug; 43 import android.os.IBinder; 44 import android.os.RemoteException; 45 import android.os.SystemClock; 46 import android.util.ArraySet; 47 import android.util.MathUtils; 48 import android.util.Slog; 49 import android.util.SparseArray; 50 import android.view.Display; 51 import android.view.DisplayInfo; 52 import android.view.SurfaceControl; 53 import android.view.WindowManager; 54 import android.window.ScreenCapture; 55 56 import com.android.internal.R; 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.protolog.ProtoLog; 59 import com.android.internal.util.ToBooleanFunction; 60 import com.android.server.wallpaper.WallpaperCropper; 61 import com.android.server.wallpaper.WallpaperDefaultDisplayInfo; 62 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.List; 66 import java.util.function.Consumer; 67 68 /** 69 * Controls wallpaper windows visibility, ordering, and so on. 70 * NOTE: All methods in this class must be called with the window manager service lock held. 71 */ 72 class WallpaperController { 73 private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM; 74 private WindowManagerService mService; 75 private DisplayContent mDisplayContent; 76 77 // Larger index has higher z-order. 78 private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>(); 79 80 // If non-null, this is the currently visible window that is associated 81 // with the wallpaper. 82 private WindowState mWallpaperTarget = null; 83 // If non-null, we are in the middle of animating from one wallpaper target 84 // to another, and this is the previous wallpaper target. 85 private WindowState mPrevWallpaperTarget = null; 86 87 private float mLastWallpaperZoomOut = 0; 88 89 // Whether COMMAND_FREEZE was dispatched. 90 private boolean mLastFrozen = false; 91 92 private float mMinWallpaperScale; 93 private float mMaxWallpaperScale; 94 95 // This is set when we are waiting for a wallpaper to tell us it is done 96 // changing its scroll position. 97 private WindowState mWaitingOnWallpaper; 98 99 // The last time we had a timeout when waiting for a wallpaper. 100 private long mLastWallpaperTimeoutTime; 101 // We give a wallpaper up to 150ms to finish scrolling. 102 private static final long WALLPAPER_TIMEOUT = 150; 103 // Time we wait after a timeout before trying to wait again. 104 private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; 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 @Nullable private Point mLargestDisplaySize = null; 114 115 private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); 116 117 private boolean mShouldOffsetWallpaperCenter; 118 119 // This is for WallpaperCropper, which has cropping logic for the default display only. 120 // This is lazily initialization by getOrCreateDefaultDisplayInfo. DO NOT use this member 121 // variable directly. 122 // TODO(b/400685784) make the WallpaperCropper operate on every display independently 123 @Nullable 124 private WallpaperDefaultDisplayInfo mDefaultDisplayInfo = null; 125 126 private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { 127 final ActivityRecord ar = w.mActivityRecord; 128 // The animating window can still be visible on screen if it is in transition, so we 129 // should check whether this window can be wallpaper target even when visibleRequested 130 // is false. 131 if (ar != null && !ar.isVisibleRequested() && !ar.isVisible()) { 132 // An activity that is not going to remain visible shouldn't be the target. 133 return false; 134 } 135 if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen() 136 + " mDrawState=" + w.mWinAnimator.mDrawState); 137 138 if (mService.mPolicy.isKeyguardLocked()) { 139 if (w.canShowWhenLocked()) { 140 if (mService.mPolicy.isKeyguardOccluded() || w.inTransition()) { 141 // The lowest show-when-locked window decides whether to show wallpaper. 142 mFindResults.mNeedsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) 143 || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent()); 144 } 145 } else if (w.hasWallpaper() && mService.mPolicy.isKeyguardHostWindow(w.mAttrs) 146 && w.mTransitionController.hasTransientLaunch(mDisplayContent)) { 147 // If we have no candidates at all, notification shade is allowed to be the target 148 // of last resort even if it has not been made visible yet. 149 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found keyguard as wallpaper target: " + w); 150 mFindResults.setWallpaperTarget(w); 151 return false; 152 } 153 } else if (mService.mFlags.mAodTransition 154 && mDisplayContent.isKeyguardLockedOrAodShowing()) { 155 if (mService.mPolicy.isKeyguardHostWindow(w.mAttrs) 156 && w.mTransitionController.isInAodAppearTransition()) { 157 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found aod transition wallpaper target: " + w); 158 mFindResults.setWallpaperTarget(w); 159 return true; 160 } 161 } 162 163 if (isBackNavigationTarget(w)) { 164 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found back animation wallpaper target: " + w); 165 mFindResults.setWallpaperTarget(w); 166 return true; 167 } else if (w.hasWallpaper() 168 && (w.mActivityRecord != null ? w.isOnScreen() : w.isReadyForDisplay())) { 169 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); 170 mFindResults.setWallpaperTarget(w); 171 mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground()); 172 if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) { 173 // The current wallpaper target is animating, so we'll look behind it for 174 // another possible target and figure out what is going on later. 175 if (DEBUG_WALLPAPER) Slog.v(TAG, 176 "Win " + w + ": token animating, looking behind."); 177 } 178 // While the keyguard is going away, both notification shade and a normal activity such 179 // as a launcher can satisfy criteria for a wallpaper target. In this case, we should 180 // chose the normal activity, otherwise wallpaper becomes invisible when a new animation 181 // starts before the keyguard going away animation finishes. 182 if (w.mActivityRecord == null && mDisplayContent.isKeyguardGoingAway()) { 183 return false; 184 } 185 return true; 186 } 187 return false; 188 }; 189 isBackNavigationTarget(WindowState w)190 private boolean isBackNavigationTarget(WindowState w) { 191 // The window is in animating by back navigation and set to show wallpaper. 192 return mService.mAtmService.mBackNavigationController.isWallpaperVisible(w); 193 } 194 195 /** 196 * @see #computeLastWallpaperZoomOut() 197 */ 198 private Consumer<WindowState> mComputeMaxZoomOutFunction = windowState -> { 199 if (!windowState.mIsWallpaper 200 && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) { 201 mLastWallpaperZoomOut = windowState.mWallpaperZoomOut; 202 } 203 }; 204 WallpaperController(WindowManagerService service, DisplayContent displayContent)205 WallpaperController(WindowManagerService service, DisplayContent displayContent) { 206 mService = service; 207 mDisplayContent = displayContent; 208 Resources resources = service.mContext.getResources(); 209 mMinWallpaperScale = 210 resources.getFloat(com.android.internal.R.dimen.config_wallpaperMinScale); 211 mMaxWallpaperScale = resources.getFloat(R.dimen.config_wallpaperMaxScale); 212 mShouldOffsetWallpaperCenter = resources.getBoolean( 213 com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay); 214 } 215 resetLargestDisplay(Display display)216 void resetLargestDisplay(Display display) { 217 if (display != null && display.getType() == Display.TYPE_INTERNAL) { 218 mLargestDisplaySize = null; 219 } 220 } 221 222 @VisibleForTesting setMinWallpaperScale(float minScale)223 void setMinWallpaperScale(float minScale) { 224 mMinWallpaperScale = minScale; 225 } 226 227 @VisibleForTesting setMaxWallpaperScale(float maxScale)228 void setMaxWallpaperScale(float maxScale) { 229 mMaxWallpaperScale = maxScale; 230 } 231 setShouldOffsetWallpaperCenter(boolean shouldOffset)232 @VisibleForTesting void setShouldOffsetWallpaperCenter(boolean shouldOffset) { 233 mShouldOffsetWallpaperCenter = shouldOffset; 234 } 235 findLargestDisplaySize()236 @Nullable private Point findLargestDisplaySize() { 237 if (!mShouldOffsetWallpaperCenter || multiCrop()) { 238 return null; 239 } 240 Point largestDisplaySize = new Point(); 241 float largestWidth = 0; 242 List<DisplayInfo> possibleDisplayInfo = 243 mService.getPossibleDisplayInfoLocked(DEFAULT_DISPLAY); 244 for (int i = 0; i < possibleDisplayInfo.size(); i++) { 245 DisplayInfo displayInfo = possibleDisplayInfo.get(i); 246 float width = (float) displayInfo.logicalWidth / displayInfo.physicalXDpi; 247 if (displayInfo.type == Display.TYPE_INTERNAL && width > largestWidth) { 248 largestWidth = width; 249 largestDisplaySize.set(displayInfo.logicalWidth, 250 displayInfo.logicalHeight); 251 } 252 } 253 return largestDisplaySize; 254 } 255 getWallpaperTarget()256 WindowState getWallpaperTarget() { 257 return mWallpaperTarget; 258 } 259 getPrevWallpaperTarget()260 WindowState getPrevWallpaperTarget() { 261 return mPrevWallpaperTarget; 262 } 263 isWallpaperTarget(WindowState win)264 boolean isWallpaperTarget(WindowState win) { 265 return win == mWallpaperTarget; 266 } 267 isBelowWallpaperTarget(WindowState win)268 boolean isBelowWallpaperTarget(WindowState win) { 269 return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer; 270 } 271 isWallpaperVisible()272 boolean isWallpaperVisible() { 273 for (int i = mWallpaperTokens.size() - 1; i >= 0; --i) { 274 if (mWallpaperTokens.get(i).isVisible()) return true; 275 } 276 return false; 277 } 278 hideDeferredWallpapersIfNeededLegacy()279 void hideDeferredWallpapersIfNeededLegacy() { 280 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 281 final WallpaperWindowToken token = mWallpaperTokens.get(i); 282 if (!token.isVisibleRequested()) { 283 token.commitVisibility(false); 284 } 285 } 286 } 287 hideWallpapers(final WindowState winGoingAway)288 void hideWallpapers(final WindowState winGoingAway) { 289 if (mWallpaperTarget != null 290 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) { 291 return; 292 } 293 if (mFindResults.useTopWallpaperAsTarget) { 294 // wallpaper target is going away but there has request to use top wallpaper 295 // Keep wallpaper visible. 296 return; 297 } 298 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 299 final WallpaperWindowToken token = mWallpaperTokens.get(i); 300 if (token.isVisible()) { 301 ProtoLog.d(WM_DEBUG_WALLPAPER, 302 "Hiding wallpaper %s from %s target=%s prev=%s callers=%s", 303 token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget, 304 Debug.getCallers(5)); 305 } 306 token.setVisibility(false); 307 } 308 } 309 updateWallpaperOffset(WindowState wallpaperWin, boolean sync)310 boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) { 311 // Size of the display the wallpaper is rendered on. 312 final Rect lastWallpaperBounds = wallpaperWin.getParentFrame(); 313 int screenWidth = lastWallpaperBounds.width(); 314 int screenHeight = lastWallpaperBounds.height(); 315 float screenRatio = (float) screenWidth / screenHeight; 316 Point screenSize = new Point(screenWidth, screenHeight); 317 318 WallpaperWindowToken token = wallpaperWin.mToken.asWallpaperToken(); 319 320 /* 321 * TODO(b/270726737) adapt comments once flag gets removed and multiCrop is always true 322 * Size of the wallpaper. May have more width/height ratio than the screen for parallax. 323 * 324 * If multiCrop is true, we use a map, cropHints, defining which sub-area of the wallpaper 325 * to show for a given screen orientation. In this case, wallpaperFrame represents the 326 * sub-area of WallpaperWin to show for the current screen size. 327 * 328 * If multiCrop is false, don't show a custom sub-area of the wallpaper. Just show the 329 * whole wallpaperWin if possible, and center and zoom if necessary. 330 */ 331 final Rect wallpaperFrame; 332 333 /* 334 * The values cropZoom, cropOffsetX and cropOffsetY are only used if multiCrop is true. 335 * Zoom and offsets to be applied in order to show wallpaperFrame on screen. 336 */ 337 final float cropZoom; 338 final int cropOffsetX; 339 final int cropOffsetY; 340 341 /* 342 * Difference of width/height between the wallpaper and the screen. 343 * This is the additional room that we have to apply offsets (i.e. parallax). 344 */ 345 final int diffWidth; 346 final int diffHeight; 347 348 /* 349 * zoom, offsetX and offsetY are not related to cropping the wallpaper: 350 * - zoom is used to apply an additional zoom (e.g. for launcher animations). 351 * - offsetX, offsetY are used to apply an offset to the wallpaper (e.g. parallax effect). 352 */ 353 final float zoom; 354 int offsetX; 355 int offsetY; 356 357 if (multiCrop()) { 358 Point bitmapSize = new Point( 359 wallpaperWin.mRequestedWidth, wallpaperWin.mRequestedHeight); 360 SparseArray<Rect> cropHints = token.getCropHints(); 361 wallpaperFrame = bitmapSize.x <= 0 || bitmapSize.y <= 0 ? wallpaperWin.getFrame() 362 : WallpaperCropper.getCrop(screenSize, getOrCreateDefaultDisplayInfo(), 363 bitmapSize, cropHints, wallpaperWin.isRtl()); 364 int frameWidth = wallpaperFrame.width(); 365 int frameHeight = wallpaperFrame.height(); 366 float frameRatio = (float) frameWidth / frameHeight; 367 368 // If the crop is proportionally wider/taller than the screen, scale it so that its 369 // height/width matches the screen height/width, and use the additional width/height 370 // for parallax (respectively). 371 boolean scaleHeight = frameRatio >= screenRatio; 372 cropZoom = wallpaperFrame.isEmpty() ? 1f : scaleHeight 373 ? (float) screenHeight / frameHeight / wallpaperWin.mVScale 374 : (float) screenWidth / frameWidth / wallpaperWin.mHScale; 375 376 // The dimensions of the frame, without the additional width or height for parallax. 377 float w = scaleHeight ? frameHeight * screenRatio : frameWidth; 378 float h = scaleHeight ? frameHeight : frameWidth / screenRatio; 379 380 // Note: a positive x/y offset shifts the wallpaper to the right/bottom respectively. 381 cropOffsetX = -wallpaperFrame.left + (int) ((cropZoom - 1f) * w / 2f); 382 cropOffsetY = -wallpaperFrame.top + (int) ((cropZoom - 1f) * h / 2f); 383 384 // Available width or height for parallax 385 diffWidth = (int) ((frameWidth - w) * wallpaperWin.mHScale); 386 diffHeight = (int) ((frameHeight - h) * wallpaperWin.mVScale); 387 } else { 388 wallpaperFrame = wallpaperWin.getFrame(); 389 cropZoom = 1f; 390 cropOffsetX = 0; 391 cropOffsetY = 0; 392 diffWidth = wallpaperFrame.width() - screenWidth; 393 diffHeight = wallpaperFrame.height() - screenHeight; 394 395 if ((wallpaperWin.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0 396 && Math.abs(diffWidth) > 1 && Math.abs(diffHeight) > 1) { 397 Slog.d(TAG, "Skip wallpaper offset with inconsistent orientation, bounds=" 398 + lastWallpaperBounds + " frame=" + wallpaperFrame); 399 // With FLAG_SCALED, the requested size should at least make the frame match one of 400 // side. If both sides contain differences, the client side may not have updated the 401 // latest size according to the current orientation. So skip calculating the offset 402 // to avoid the wallpaper not filling the screen. 403 return false; 404 } 405 } 406 407 boolean rawChanged = false; 408 // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to 409 // match the behavior of most Launchers 410 float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f; 411 // "Wallpaper X" is the previous x-offset of the wallpaper (in a 0 to 1 scale). 412 // The 0 to 1 scale is because the "length" varies depending on how many home screens you 413 // have, so 0 is the left of the first home screen, and 1 is the right of the last one (for 414 // LTR, and the opposite for RTL). 415 float wpx = token.mWallpaperX >= 0 ? token.mWallpaperX : defaultWallpaperX; 416 // "Wallpaper X step size" is how much of that 0-1 is one "page" of the home screen 417 // when scrolling. 418 float wpxs = token.mWallpaperXStep >= 0 ? token.mWallpaperXStep : -1.0f; 419 // Difference between width of wallpaper image, and the last size of the wallpaper. 420 // This is the horizontal surplus from the prior configuration. 421 int availw = diffWidth; 422 423 int displayOffset = getDisplayWidthOffset(availw, lastWallpaperBounds, 424 wallpaperWin.isRtl()); 425 availw -= displayOffset; 426 offsetX = availw > 0 ? -(int) (availw * wpx + .5f) : 0; 427 if (token.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 428 // if device is LTR, then offset wallpaper to the left (the wallpaper is drawn 429 // always starting from the left of the screen). 430 offsetX += token.mWallpaperDisplayOffsetX; 431 } else if (!wallpaperWin.isRtl()) { 432 // In RTL the offset is calculated so that the wallpaper ends up right aligned (see 433 // offset above). 434 offsetX -= displayOffset; 435 } 436 offsetX += cropOffsetX * wallpaperWin.mHScale; 437 438 if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { 439 wallpaperWin.mWallpaperX = wpx; 440 wallpaperWin.mWallpaperXStep = wpxs; 441 rawChanged = true; 442 } 443 444 float wpy = token.mWallpaperY >= 0 ? token.mWallpaperY : 0.5f; 445 float wpys = token.mWallpaperYStep >= 0 ? token.mWallpaperYStep : -1.0f; 446 offsetY = diffHeight > 0 ? -(int) (diffHeight * wpy + .5f) : 0; 447 if (token.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 448 offsetY += token.mWallpaperDisplayOffsetY; 449 } 450 offsetY += cropOffsetY * wallpaperWin.mVScale; 451 452 if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { 453 wallpaperWin.mWallpaperY = wpy; 454 wallpaperWin.mWallpaperYStep = wpys; 455 rawChanged = true; 456 } 457 458 if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) { 459 wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut; 460 rawChanged = true; 461 } 462 zoom = wallpaperWin.mShouldScaleWallpaper 463 ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1f; 464 final float totalZoom = zoom * cropZoom; 465 boolean changed = wallpaperWin.setWallpaperOffset(offsetX, offsetY, totalZoom); 466 467 if (rawChanged && (wallpaperWin.mAttrs.privateFlags & 468 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { 469 try { 470 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " 471 + wallpaperWin + " x=" + wallpaperWin.mWallpaperX 472 + " y=" + wallpaperWin.mWallpaperY 473 + " zoom=" + wallpaperWin.mWallpaperZoomOut); 474 if (sync) { 475 mWaitingOnWallpaper = wallpaperWin; 476 } 477 wallpaperWin.mClient.dispatchWallpaperOffsets( 478 wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, 479 wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, 480 wallpaperWin.mWallpaperZoomOut, sync); 481 482 if (sync) { 483 if (mWaitingOnWallpaper != null) { 484 long start = SystemClock.uptimeMillis(); 485 if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY) 486 < start) { 487 try { 488 ProtoLog.v(WM_DEBUG_WALLPAPER, "Waiting for offset complete..."); 489 mService.mGlobalLock.wait(WALLPAPER_TIMEOUT); 490 } catch (InterruptedException e) { 491 } 492 ProtoLog.v(WM_DEBUG_WALLPAPER, "Offset complete!"); 493 if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) { 494 ProtoLog.v(WM_DEBUG_WALLPAPER, 495 "Timeout waiting for wallpaper to offset: %s", 496 wallpaperWin); 497 mLastWallpaperTimeoutTime = start; 498 } 499 } 500 mWaitingOnWallpaper = null; 501 } 502 } 503 } catch (RemoteException e) { 504 } 505 } 506 507 return changed; 508 } 509 getOrCreateDefaultDisplayInfo()510 private WallpaperDefaultDisplayInfo getOrCreateDefaultDisplayInfo() { 511 if (mDefaultDisplayInfo != null) { 512 return mDefaultDisplayInfo; 513 } 514 WindowManager windowManager = mService.mContext.getSystemService(WindowManager.class); 515 Resources resources = mService.mContext.getResources(); 516 mDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(windowManager, resources); 517 return mDefaultDisplayInfo; 518 } 519 520 /** 521 * Get an extra offset if needed ({@link #mShouldOffsetWallpaperCenter} = true, typically on 522 * multiple display devices) so that the wallpaper in a smaller display ends up centered at the 523 * same position as in the largest display of the device. 524 * 525 * Note that the wallpaper has already been cropped when set by the user, so these calculations 526 * apply to the image size for the display the wallpaper was set for. 527 * 528 * @param availWidth width available for the wallpaper offset in the current display 529 * @param displayFrame size of the "display" (parent frame) 530 * @param isRtl whether we're in an RTL configuration 531 * @return an offset to apply to the width, or 0 if the current configuration doesn't require 532 * any adjustment (either @link #mShouldOffsetWallpaperCenter} is false or we're on the largest 533 * display). 534 */ getDisplayWidthOffset(int availWidth, Rect displayFrame, boolean isRtl)535 private int getDisplayWidthOffset(int availWidth, Rect displayFrame, boolean isRtl) { 536 if (!mShouldOffsetWallpaperCenter || multiCrop()) { 537 return 0; 538 } 539 if (mLargestDisplaySize == null) { 540 mLargestDisplaySize = findLargestDisplaySize(); 541 } 542 if (mLargestDisplaySize == null) { 543 return 0; 544 } 545 // Page width is the width of a Launcher "page", for pagination when swiping right. 546 int pageWidth = displayFrame.width(); 547 // Only need offset if the current size is different from the largest display, and we're 548 // in a portrait configuration 549 if (mLargestDisplaySize.x != pageWidth && displayFrame.width() < displayFrame.height()) { 550 // The wallpaper will be scaled to fit the height of the wallpaper, so if the height 551 // of the displays are different, we need to account for that scaling when calculating 552 // the offset to the center 553 float sizeRatio = (float) displayFrame.height() / mLargestDisplaySize.y; 554 // Scale the width of the largest display to match the scale of the wallpaper size in 555 // the current display 556 int adjustedLargestWidth = Math.round(mLargestDisplaySize.x * sizeRatio); 557 // Finally, find the difference between the centers, taking into account that the 558 // size of the wallpaper frame could be smaller than the screen 559 return isRtl 560 ? adjustedLargestWidth - (adjustedLargestWidth + pageWidth) / 2 561 : Math.min(adjustedLargestWidth - pageWidth, availWidth) / 2; 562 } 563 return 0; 564 } 565 setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)566 void setWindowWallpaperPosition( 567 WindowState window, float x, float y, float xStep, float yStep) { 568 if (window.mWallpaperX != x || window.mWallpaperY != y) { 569 window.mWallpaperX = x; 570 window.mWallpaperY = y; 571 window.mWallpaperXStep = xStep; 572 window.mWallpaperYStep = yStep; 573 updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync); 574 } 575 } 576 setWallpaperZoomOut(WindowState window, float zoom)577 void setWallpaperZoomOut(WindowState window, float zoom) { 578 if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) { 579 window.mWallpaperZoomOut = zoom; 580 computeLastWallpaperZoomOut(); 581 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 582 final WallpaperWindowToken token = mWallpaperTokens.get(i); 583 token.updateWallpaperOffset(false); 584 } 585 } 586 } 587 setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom)588 void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) { 589 if (shouldZoom != window.mShouldScaleWallpaper) { 590 window.mShouldScaleWallpaper = shouldZoom; 591 updateWallpaperOffsetLocked(window, false); 592 } 593 } 594 setWindowWallpaperDisplayOffset(WindowState window, int x, int y)595 void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) { 596 if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) { 597 window.mWallpaperDisplayOffsetX = x; 598 window.mWallpaperDisplayOffsetY = y; 599 updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync); 600 } 601 } 602 sendWindowWallpaperCommandUnchecked( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)603 void sendWindowWallpaperCommandUnchecked( 604 WindowState window, String action, int x, int y, int z, 605 Bundle extras, boolean sync) { 606 sendWindowWallpaperCommand(action, x, y, z, extras, sync); 607 } 608 sendWindowWallpaperCommand( String action, int x, int y, int z, Bundle extras, boolean sync)609 private void sendWindowWallpaperCommand( 610 String action, int x, int y, int z, Bundle extras, boolean sync) { 611 boolean doWait = sync; 612 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 613 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 614 token.sendWindowWallpaperCommand(action, x, y, z, extras, sync); 615 } 616 617 if (doWait) { 618 // TODO: Need to wait for result. 619 } 620 } 621 updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)622 private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { 623 WindowState target = mWallpaperTarget; 624 if (target == null && changingTarget.mToken.isVisible() 625 && changingTarget.mTransitionController.inTransition()) { 626 // If the wallpaper target was cleared during transition, still allows the visible 627 // window which may have been requested to be invisible to update the offset, e.g. 628 // zoom effect from home. 629 target = changingTarget; 630 } 631 632 WallpaperWindowToken token = getTokenForTarget(target); 633 if (token == null) return; 634 635 if (target.mWallpaperX >= 0) { 636 token.mWallpaperX = target.mWallpaperX; 637 } else if (changingTarget.mWallpaperX >= 0) { 638 token.mWallpaperX = changingTarget.mWallpaperX; 639 } 640 if (target.mWallpaperY >= 0) { 641 token.mWallpaperY = target.mWallpaperY; 642 } else if (changingTarget.mWallpaperY >= 0) { 643 token.mWallpaperY = changingTarget.mWallpaperY; 644 } 645 if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 646 token.mWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX; 647 } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 648 token.mWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX; 649 } 650 if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 651 token.mWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY; 652 } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 653 token.mWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY; 654 } 655 if (target.mWallpaperXStep >= 0) { 656 token.mWallpaperXStep = target.mWallpaperXStep; 657 } else if (changingTarget.mWallpaperXStep >= 0) { 658 token.mWallpaperXStep = changingTarget.mWallpaperXStep; 659 } 660 if (target.mWallpaperYStep >= 0) { 661 token.mWallpaperYStep = target.mWallpaperYStep; 662 } else if (changingTarget.mWallpaperYStep >= 0) { 663 token.mWallpaperYStep = changingTarget.mWallpaperYStep; 664 } 665 token.updateWallpaperOffset(sync); 666 } 667 getTokenForTarget(WindowState target)668 private WallpaperWindowToken getTokenForTarget(WindowState target) { 669 if (target == null) return null; 670 WindowState window = mFindResults.getTopWallpaper( 671 (target.canShowWhenLocked() && mService.isKeyguardLocked()) 672 || (mService.mFlags.mAodTransition && mDisplayContent.isAodShowing())); 673 return window == null ? null : window.mToken.asWallpaperToken(); 674 } 675 clearLastWallpaperTimeoutTime()676 void clearLastWallpaperTimeoutTime() { 677 mLastWallpaperTimeoutTime = 0; 678 } 679 wallpaperCommandComplete(IBinder window)680 void wallpaperCommandComplete(IBinder window) { 681 if (mWaitingOnWallpaper != null && 682 mWaitingOnWallpaper.mClient.asBinder() == window) { 683 mWaitingOnWallpaper = null; 684 mService.mGlobalLock.notifyAll(); 685 } 686 } 687 wallpaperOffsetsComplete(IBinder window)688 void wallpaperOffsetsComplete(IBinder window) { 689 if (mWaitingOnWallpaper != null && 690 mWaitingOnWallpaper.mClient.asBinder() == window) { 691 mWaitingOnWallpaper = null; 692 mService.mGlobalLock.notifyAll(); 693 } 694 } 695 findWallpaperTarget()696 private void findWallpaperTarget() { 697 mFindResults.reset(); 698 if (mService.mAtmService.mSupportsFreeformWindowManagement 699 && mDisplayContent.getDefaultTaskDisplayArea() 700 .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) { 701 // In freeform mode we set the wallpaper as its own target, so we don't need an 702 // additional window to make it visible. 703 mFindResults.setUseTopWallpaperAsTarget(true); 704 } 705 706 findWallpapers(); 707 mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); 708 if (mFindResults.mNeedsShowWhenLockedWallpaper) { 709 // Keep wallpaper visible if the show-when-locked activities doesn't fill screen. 710 mFindResults.setUseTopWallpaperAsTarget(true); 711 } 712 713 if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { 714 mFindResults.setWallpaperTarget( 715 mFindResults.getTopWallpaper(mService.mFlags.mAodTransition 716 ? mDisplayContent.isKeyguardLockedOrAodShowing() 717 : mDisplayContent.isKeyguardLocked())); 718 } 719 } 720 findWallpapers()721 private void findWallpapers() { 722 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 723 final WallpaperWindowToken token = mWallpaperTokens.get(i); 724 final boolean canShowWhenLocked = token.canShowWhenLocked(); 725 for (int j = token.getChildCount() - 1; j >= 0; j--) { 726 final WindowState w = token.getChildAt(j); 727 if (!w.mIsWallpaper) continue; 728 if (canShowWhenLocked && !mFindResults.hasTopShowWhenLockedWallpaper()) { 729 mFindResults.setTopShowWhenLockedWallpaper(w); 730 } else if (!canShowWhenLocked && !mFindResults.hasTopHideWhenLockedWallpaper()) { 731 mFindResults.setTopHideWhenLockedWallpaper(w); 732 } 733 } 734 } 735 } 736 collectTopWallpapers(Transition transition)737 void collectTopWallpapers(Transition transition) { 738 if (mFindResults.hasTopShowWhenLockedWallpaper()) { 739 if (mService.mFlags.mEnsureWallpaperInTransitions) { 740 transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper.mToken); 741 } else { 742 transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper); 743 } 744 745 } 746 if (mFindResults.hasTopHideWhenLockedWallpaper()) { 747 if (mService.mFlags.mEnsureWallpaperInTransitions) { 748 transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper.mToken); 749 } else { 750 transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper); 751 } 752 } 753 } 754 isFullscreen(WindowManager.LayoutParams attrs)755 private boolean isFullscreen(WindowManager.LayoutParams attrs) { 756 return attrs.x == 0 && attrs.y == 0 757 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT; 758 } 759 760 /** Updates the target wallpaper if needed and returns true if an update happened. */ updateWallpaperWindowsTarget(FindWallpaperTargetResult result)761 private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) { 762 763 WindowState wallpaperTarget = result.wallpaperTarget; 764 765 if (mWallpaperTarget == wallpaperTarget 766 || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) { 767 768 if (mPrevWallpaperTarget == null) { 769 return; 770 } 771 772 // Is it time to stop animating? 773 if (!mPrevWallpaperTarget.isAnimatingLw()) { 774 ProtoLog.v(WM_DEBUG_WALLPAPER, "No longer animating wallpaper targets!"); 775 mPrevWallpaperTarget = null; 776 mWallpaperTarget = wallpaperTarget; 777 } 778 return; 779 } 780 781 ProtoLog.v(WM_DEBUG_WALLPAPER, "New wallpaper target: %s prevTarget: %s caller=%s", 782 wallpaperTarget, mWallpaperTarget, Debug.getCallers(5)); 783 784 mPrevWallpaperTarget = null; 785 786 final WindowState prevWallpaperTarget = mWallpaperTarget; 787 mWallpaperTarget = wallpaperTarget; 788 789 if (prevWallpaperTarget == null && wallpaperTarget != null) { 790 updateWallpaperOffsetLocked(mWallpaperTarget, false); 791 } 792 if (wallpaperTarget == null || prevWallpaperTarget == null) { 793 return; 794 } 795 796 // Now what is happening... if the current and new targets are animating, 797 // then we are in our super special mode! 798 boolean oldAnim = prevWallpaperTarget.isAnimatingLw(); 799 boolean foundAnim = wallpaperTarget.isAnimatingLw(); 800 ProtoLog.v(WM_DEBUG_WALLPAPER, "New animation: %s old animation: %s", 801 foundAnim, oldAnim); 802 803 if (!foundAnim || !oldAnim) { 804 return; 805 } 806 807 if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) { 808 return; 809 } 810 811 final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null 812 && !wallpaperTarget.mActivityRecord.isVisibleRequested(); 813 final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null 814 && !prevWallpaperTarget.mActivityRecord.isVisibleRequested(); 815 816 ProtoLog.v(WM_DEBUG_WALLPAPER, "Animating wallpapers: " 817 + "old: %s hidden=%b new: %s hidden=%b", 818 prevWallpaperTarget, oldTargetHidden, wallpaperTarget, newTargetHidden); 819 820 mPrevWallpaperTarget = prevWallpaperTarget; 821 822 if (newTargetHidden && !oldTargetHidden) { 823 ProtoLog.v(WM_DEBUG_WALLPAPER, "Old wallpaper still the target."); 824 // Use the old target if new target is hidden but old target 825 // is not. If they're both hidden, still use the new target. 826 mWallpaperTarget = prevWallpaperTarget; 827 } 828 829 result.setWallpaperTarget(wallpaperTarget); 830 } 831 832 /** 833 * Change the visibility of the top wallpaper to {@param visibility} and hide all the others. 834 */ updateWallpaperTokens(boolean visibility, boolean keyguardLocked)835 private void updateWallpaperTokens(boolean visibility, boolean keyguardLocked) { 836 ProtoLog.v(WM_DEBUG_WALLPAPER, "updateWallpaperTokens requestedVisibility=%b on" 837 + " keyguardLocked=%b", visibility, keyguardLocked); 838 WindowState topWallpaper = mFindResults.getTopWallpaper(keyguardLocked); 839 WallpaperWindowToken topWallpaperToken = 840 topWallpaper == null ? null : topWallpaper.mToken.asWallpaperToken(); 841 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 842 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 843 token.updateWallpaperWindows(visibility && (token == topWallpaperToken)); 844 } 845 } 846 adjustWallpaperWindows()847 void adjustWallpaperWindows() { 848 mDisplayContent.mWallpaperMayChange = false; 849 850 // First find top-most window that has asked to be on top of the wallpaper; 851 // all wallpapers go behind it. 852 findWallpaperTarget(); 853 updateWallpaperWindowsTarget(mFindResults); 854 WallpaperWindowToken token = getTokenForTarget(mWallpaperTarget); 855 856 // The window is visible to the compositor...but is it visible to the user? 857 // That is what the wallpaper cares about. 858 final boolean visible = token != null; 859 860 if (visible) { 861 if (mWallpaperTarget.mWallpaperX >= 0) { 862 token.mWallpaperX = mWallpaperTarget.mWallpaperX; 863 token.mWallpaperXStep = mWallpaperTarget.mWallpaperXStep; 864 } 865 if (mWallpaperTarget.mWallpaperY >= 0) { 866 token.mWallpaperY = mWallpaperTarget.mWallpaperY; 867 token.mWallpaperYStep = mWallpaperTarget.mWallpaperYStep; 868 } 869 if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 870 token.mWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; 871 } 872 if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 873 token.mWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; 874 } 875 } 876 877 boolean visibleRequested = visible; 878 if (mDisplayContent.mWmService.mFlags.mEnsureWallpaperInTransitions) { 879 visibleRequested = mWallpaperTarget != null && mWallpaperTarget.isVisibleRequested(); 880 } 881 updateWallpaperTokens(visibleRequested, 882 mService.mFlags.mAodTransition 883 ? mDisplayContent.isKeyguardLockedOrAodShowing() 884 : mDisplayContent.isKeyguardLocked()); 885 886 ProtoLog.v(WM_DEBUG_WALLPAPER, 887 "Wallpaper at display %d - visibility: %b, keyguardLocked: %b", 888 mDisplayContent.getDisplayId(), visible, 889 mService.mFlags.mAodTransition 890 ? mDisplayContent.isKeyguardLockedOrAodShowing() 891 : mDisplayContent.isKeyguardLocked()); 892 893 if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) { 894 mLastFrozen = mFindResults.isWallpaperTargetForLetterbox; 895 sendWindowWallpaperCommand( 896 mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE, 897 /* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false); 898 } 899 900 ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper target=%s prev=%s", 901 mWallpaperTarget, mPrevWallpaperTarget); 902 } 903 processWallpaperDrawPendingTimeout()904 boolean processWallpaperDrawPendingTimeout() { 905 if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) { 906 mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT; 907 if (DEBUG_WALLPAPER) { 908 Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT"); 909 } 910 911 // If there was a pending back navigation animation that would show wallpaper, start 912 // the animation due to it was skipped in previous surface placement. 913 mService.mAtmService.mBackNavigationController.startAnimation(); 914 return true; 915 } 916 return false; 917 } 918 wallpaperTransitionReady()919 boolean wallpaperTransitionReady() { 920 boolean transitionReady = true; 921 boolean wallpaperReady = true; 922 for (int curTokenIndex = mWallpaperTokens.size() - 1; 923 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) { 924 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex); 925 if (token.hasVisibleNotDrawnWallpaper()) { 926 // We've told this wallpaper to be visible, but it is not drawn yet 927 wallpaperReady = false; 928 if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { 929 // wait for this wallpaper until it is drawn or timeout 930 transitionReady = false; 931 } 932 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { 933 mWallpaperDrawState = WALLPAPER_DRAW_PENDING; 934 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 935 mService.mH.sendMessageDelayed( 936 mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this), 937 WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); 938 939 } 940 ProtoLog.v(WM_DEBUG_WALLPAPER, 941 "Wallpaper should be visible but has not been drawn yet. " 942 + "mWallpaperDrawState=%d", mWallpaperDrawState); 943 break; 944 } 945 } 946 if (wallpaperReady) { 947 mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; 948 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 949 } 950 951 return transitionReady; 952 } 953 954 /** 955 * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of 956 * the opening apps should be a wallpaper target. 957 */ adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps)958 void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) { 959 boolean adjust = false; 960 if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { 961 adjust = true; 962 } else { 963 for (int i = openingApps.size() - 1; i >= 0; --i) { 964 final ActivityRecord activity = openingApps.valueAt(i); 965 if (activity.windowsCanBeWallpaperTarget()) { 966 adjust = true; 967 break; 968 } 969 } 970 } 971 972 if (adjust) { 973 adjustWallpaperWindows(); 974 } 975 } 976 addWallpaperToken(WallpaperWindowToken token)977 void addWallpaperToken(WallpaperWindowToken token) { 978 mWallpaperTokens.add(token); 979 } 980 removeWallpaperToken(WallpaperWindowToken token)981 void removeWallpaperToken(WallpaperWindowToken token) { 982 mWallpaperTokens.remove(token); 983 } 984 onWallpaperTokenReordered()985 void onWallpaperTokenReordered() { 986 if (mWallpaperTokens.size() > 1) { 987 mWallpaperTokens.sort(null /* by WindowContainer#compareTo */); 988 } 989 } 990 991 @VisibleForTesting canScreenshotWallpaper()992 boolean canScreenshotWallpaper() { 993 return canScreenshotWallpaper(getTopVisibleWallpaper()); 994 } 995 canScreenshotWallpaper(WindowState wallpaperWindowState)996 private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) { 997 if (!mService.mPolicy.isScreenOn()) { 998 if (DEBUG_SCREENSHOT) { 999 Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); 1000 } 1001 return false; 1002 } 1003 1004 if (wallpaperWindowState == null) { 1005 if (DEBUG_SCREENSHOT) { 1006 Slog.i(TAG_WM, "No visible wallpaper to screenshot"); 1007 } 1008 return false; 1009 } 1010 return true; 1011 } 1012 1013 /** 1014 * Take a screenshot of the wallpaper if it's visible. 1015 * 1016 * @return Bitmap of the wallpaper 1017 */ screenshotWallpaperLocked()1018 Bitmap screenshotWallpaperLocked() { 1019 final WindowState wallpaperWindowState = getTopVisibleWallpaper(); 1020 if (!canScreenshotWallpaper(wallpaperWindowState)) { 1021 return null; 1022 } 1023 1024 final Rect bounds = wallpaperWindowState.getBounds(); 1025 bounds.offsetTo(0, 0); 1026 1027 ScreenCapture.ScreenshotHardwareBuffer wallpaperBuffer = ScreenCapture.captureLayers( 1028 wallpaperWindowState.getSurfaceControl(), bounds, 1 /* frameScale */); 1029 1030 if (wallpaperBuffer == null) { 1031 Slog.w(TAG_WM, "Failed to screenshot wallpaper"); 1032 return null; 1033 } 1034 return Bitmap.wrapHardwareBuffer( 1035 wallpaperBuffer.getHardwareBuffer(), wallpaperBuffer.getColorSpace()); 1036 } 1037 1038 /** 1039 * Mirrors the visible wallpaper if it's available. 1040 * <p> 1041 * We mirror at the WallpaperWindowToken level because scale and translation is applied at 1042 * the WindowState level and mirroring the WindowState's SurfaceControl will remove any local 1043 * scale and translation. 1044 * 1045 * @return A SurfaceControl for the parent of the mirrored wallpaper. 1046 */ mirrorWallpaperSurface()1047 SurfaceControl mirrorWallpaperSurface() { 1048 final WindowState wallpaperWindowState = getTopVisibleWallpaper(); 1049 return wallpaperWindowState != null 1050 ? SurfaceControl.mirrorSurface(wallpaperWindowState.mToken.getSurfaceControl()) 1051 : null; 1052 } 1053 getTopVisibleWallpaper()1054 WindowState getTopVisibleWallpaper() { 1055 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 1056 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 1057 for (int i = token.getChildCount() - 1; i >= 0; i--) { 1058 final WindowState w = token.getChildAt(i); 1059 if (w.mWinAnimator.getShown() && w.mWinAnimator.mLastAlpha > 0f) { 1060 return w; 1061 } 1062 } 1063 } 1064 return null; 1065 } 1066 1067 /** 1068 * Each window can request a zoom, example: 1069 * - User is in overview, zoomed out. 1070 * - User also pulls down the shade. 1071 * 1072 * This means that we always have to choose the largest zoom out that we have, otherwise 1073 * we'll have conflicts and break the "depth system" mental model. 1074 */ computeLastWallpaperZoomOut()1075 private void computeLastWallpaperZoomOut() { 1076 mLastWallpaperZoomOut = 0; 1077 mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true); 1078 } 1079 1080 zoomOutToScale(float zoomOut)1081 private float zoomOutToScale(float zoomOut) { 1082 return MathUtils.lerp(mMinWallpaperScale, mMaxWallpaperScale, 1 - zoomOut); 1083 } 1084 dump(PrintWriter pw, String prefix)1085 void dump(PrintWriter pw, String prefix) { 1086 pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId()); 1087 pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget); 1088 pw.print(prefix); pw.print("mLastWallpaperZoomOut="); pw.println(mLastWallpaperZoomOut); 1089 if (mPrevWallpaperTarget != null) { 1090 pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget); 1091 } 1092 1093 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 1094 final WallpaperWindowToken t = mWallpaperTokens.get(i); 1095 pw.print(prefix); pw.println("token " + t + ":"); 1096 dumpValue(pw, prefix, "mWallpaperX", t.mWallpaperX); 1097 dumpValue(pw, prefix, "mWallpaperY", t.mWallpaperY); 1098 dumpValue(pw, prefix, "mWallpaperXStep", t.mWallpaperXStep); 1099 dumpValue(pw, prefix, "mWallpaperYStep", t.mWallpaperYStep); 1100 dumpValue(pw, prefix, "mWallpaperDisplayOffsetX", t.mWallpaperDisplayOffsetX); 1101 dumpValue(pw, prefix, "mWallpaperDisplayOffsetY", t.mWallpaperDisplayOffsetY); 1102 } 1103 } 1104 dumpValue(PrintWriter pw, String prefix, String valueName, float value)1105 private void dumpValue(PrintWriter pw, String prefix, String valueName, float value) { 1106 pw.print(prefix); pw.print(" " + valueName + "="); 1107 pw.println(value >= 0 ? value : "NA"); 1108 } 1109 1110 /** Helper class for storing the results of a wallpaper target find operation. */ 1111 final private static class FindWallpaperTargetResult { 1112 1113 static final class TopWallpaper { 1114 // A wp that can be visible on home screen only 1115 WindowState mTopHideWhenLockedWallpaper = null; 1116 // A wallpaper that has permission to be visible on lock screen (lock or shared wp) 1117 WindowState mTopShowWhenLockedWallpaper = null; 1118 reset()1119 void reset() { 1120 mTopHideWhenLockedWallpaper = null; 1121 mTopShowWhenLockedWallpaper = null; 1122 } 1123 } 1124 1125 TopWallpaper mTopWallpaper = new TopWallpaper(); 1126 boolean mNeedsShowWhenLockedWallpaper; 1127 boolean useTopWallpaperAsTarget = false; 1128 WindowState wallpaperTarget = null; 1129 boolean isWallpaperTargetForLetterbox = false; 1130 setTopHideWhenLockedWallpaper(WindowState win)1131 void setTopHideWhenLockedWallpaper(WindowState win) { 1132 if (mTopWallpaper.mTopHideWhenLockedWallpaper != win) { 1133 ProtoLog.d(WM_DEBUG_WALLPAPER, "New home screen wallpaper: %s, prev: %s", 1134 win, mTopWallpaper.mTopHideWhenLockedWallpaper); 1135 } 1136 mTopWallpaper.mTopHideWhenLockedWallpaper = win; 1137 } 1138 setTopShowWhenLockedWallpaper(WindowState win)1139 void setTopShowWhenLockedWallpaper(WindowState win) { 1140 if (mTopWallpaper.mTopShowWhenLockedWallpaper != win) { 1141 ProtoLog.d(WM_DEBUG_WALLPAPER, "New lock/shared screen wallpaper: %s, prev: %s", 1142 win, mTopWallpaper.mTopShowWhenLockedWallpaper); 1143 } 1144 mTopWallpaper.mTopShowWhenLockedWallpaper = win; 1145 } 1146 hasTopHideWhenLockedWallpaper()1147 boolean hasTopHideWhenLockedWallpaper() { 1148 return mTopWallpaper.mTopHideWhenLockedWallpaper != null; 1149 } 1150 hasTopShowWhenLockedWallpaper()1151 boolean hasTopShowWhenLockedWallpaper() { 1152 return mTopWallpaper.mTopShowWhenLockedWallpaper != null; 1153 } 1154 getTopWallpaper(boolean isKeyguardLocked)1155 WindowState getTopWallpaper(boolean isKeyguardLocked) { 1156 if (!isKeyguardLocked && hasTopHideWhenLockedWallpaper()) { 1157 return mTopWallpaper.mTopHideWhenLockedWallpaper; 1158 } else { 1159 return mTopWallpaper.mTopShowWhenLockedWallpaper; 1160 } 1161 } 1162 setWallpaperTarget(WindowState win)1163 void setWallpaperTarget(WindowState win) { 1164 wallpaperTarget = win; 1165 } 1166 setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget)1167 void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) { 1168 useTopWallpaperAsTarget = topWallpaperAsTarget; 1169 } 1170 setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox)1171 void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) { 1172 this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox; 1173 } 1174 reset()1175 void reset() { 1176 mTopWallpaper.reset(); 1177 mNeedsShowWhenLockedWallpaper = false; 1178 wallpaperTarget = null; 1179 useTopWallpaperAsTarget = false; 1180 isWallpaperTargetForLetterbox = false; 1181 } 1182 } 1183 } 1184