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