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