• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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