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