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