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.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
22 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
23 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
24 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
25 
26 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
27 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
28 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
31 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
33 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
34 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
35 
36 import android.graphics.Bitmap;
37 import android.graphics.Rect;
38 import android.os.Bundle;
39 import android.os.Debug;
40 import android.os.IBinder;
41 import android.os.RemoteException;
42 import android.os.SystemClock;
43 import android.util.ArraySet;
44 import android.util.MathUtils;
45 import android.util.Slog;
46 import android.view.DisplayInfo;
47 import android.view.SurfaceControl;
48 import android.view.WindowManager;
49 import android.view.animation.Animation;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.util.ToBooleanFunction;
53 
54 import java.io.PrintWriter;
55 import java.util.ArrayList;
56 import java.util.function.Consumer;
57 
58 /**
59  * Controls wallpaper windows visibility, ordering, and so on.
60  * NOTE: All methods in this class must be called with the window manager service lock held.
61  */
62 class WallpaperController {
63     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
64     private WindowManagerService mService;
65     private final DisplayContent mDisplayContent;
66 
67     private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
68 
69     // If non-null, this is the currently visible window that is associated
70     // with the wallpaper.
71     private WindowState mWallpaperTarget = null;
72     // If non-null, we are in the middle of animating from one wallpaper target
73     // to another, and this is the previous wallpaper target.
74     private WindowState mPrevWallpaperTarget = null;
75 
76     private float mLastWallpaperX = -1;
77     private float mLastWallpaperY = -1;
78     private float mLastWallpaperXStep = -1;
79     private float mLastWallpaperYStep = -1;
80     private float mLastWallpaperZoomOut = 0;
81     private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
82     private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
83     private final float mMaxWallpaperScale;
84 
85     // This is set when we are waiting for a wallpaper to tell us it is done
86     // changing its scroll position.
87     private WindowState mWaitingOnWallpaper;
88 
89     // The last time we had a timeout when waiting for a wallpaper.
90     private long mLastWallpaperTimeoutTime;
91     // We give a wallpaper up to 150ms to finish scrolling.
92     private static final long WALLPAPER_TIMEOUT = 150;
93     // Time we wait after a timeout before trying to wait again.
94     private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
95 
96     // Set to the wallpaper window we would like to hide once the transition animations are done.
97     // This is useful in cases where we don't want the wallpaper to be hidden when the close app
98     // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
99     // target and isn't done animating in.
100     WindowState mDeferredHideWallpaper = null;
101 
102     // We give a wallpaper up to 500ms to finish drawing before playing app transitions.
103     private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500;
104     private static final int WALLPAPER_DRAW_NORMAL = 0;
105     private static final int WALLPAPER_DRAW_PENDING = 1;
106     private static final int WALLPAPER_DRAW_TIMEOUT = 2;
107     private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
108 
109     private boolean mShouldUpdateZoom;
110 
111     /**
112      * Temporary storage for taking a screenshot of the wallpaper.
113      * @see #screenshotWallpaperLocked()
114      */
115     private WindowState mTmpTopWallpaper;
116 
117     private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
118 
119     private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
120         if ((w.mAttrs.type == TYPE_WALLPAPER)) {
121             if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
122                 mFindResults.setTopWallpaper(w);
123                 mFindResults.resetTopWallpaper = false;
124             }
125             return false;
126         }
127 
128         mFindResults.resetTopWallpaper = true;
129         if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
130                 && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
131 
132             // If this window's app token is hidden and not animating, it is of no interest to us.
133             if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
134             return false;
135         }
136         if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
137                 + " mDrawState=" + w.mWinAnimator.mDrawState);
138 
139         if (w.mWillReplaceWindow && mWallpaperTarget == null
140                 && !mFindResults.useTopWallpaperAsTarget) {
141             // When we are replacing a window and there was wallpaper before replacement, we want to
142             // keep the window until the new windows fully appear and can determine the visibility,
143             // to avoid flickering.
144             mFindResults.setUseTopWallpaperAsTarget(true);
145         }
146 
147         final WindowContainer animatingContainer = w.mActivityRecord != null
148                 ? w.mActivityRecord.getAnimatingContainer() : null;
149         final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null
150                 && animatingContainer.isAnimating(TRANSITION | PARENTS)
151                 && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit)
152                 && (animatingContainer.mTransitFlags
153                 & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
154 
155         boolean needsShowWhenLockedWallpaper = false;
156         if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
157                 && mService.mPolicy.isKeyguardLocked()
158                 && mService.mPolicy.isKeyguardOccluded()) {
159             // The lowest show when locked window decides whether we need to put the wallpaper
160             // behind.
161             needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs)
162                     || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent());
163         }
164 
165         if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) {
166             // Keep the wallpaper during Keyguard exit but also when it's needed for a
167             // non-fullscreen show when locked activity.
168             mFindResults.setUseTopWallpaperAsTarget(true);
169         }
170 
171         final RecentsAnimationController recentsAnimationController =
172                 mService.getRecentsAnimationController();
173         final boolean animationWallpaper = animatingContainer != null
174                 && animatingContainer.getAnimation() != null
175                 && animatingContainer.getAnimation().getShowWallpaper();
176         final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
177                 || animationWallpaper;
178         final boolean isRecentsTransitionTarget = (recentsAnimationController != null
179                 && recentsAnimationController.isWallpaperVisible(w));
180         if (isRecentsTransitionTarget) {
181             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
182             mFindResults.setWallpaperTarget(w);
183             return true;
184         } else if (hasWallpaper && w.isOnScreen()
185                 && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
186             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
187             mFindResults.setWallpaperTarget(w);
188             if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) {
189                 // The current wallpaper target is animating, so we'll look behind it for
190                 // another possible target and figure out what is going on later.
191                 if (DEBUG_WALLPAPER) Slog.v(TAG,
192                         "Win " + w + ": token animating, looking behind.");
193             }
194             // Found a target! End search.
195             return true;
196         }
197         return false;
198     };
199 
200     /**
201      * @see #computeLastWallpaperZoomOut()
202      */
203     private Consumer<WindowState>  mComputeMaxZoomOutFunction = windowState -> {
204         if (!windowState.mIsWallpaper
205                 && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) {
206             mLastWallpaperZoomOut = windowState.mWallpaperZoomOut;
207         }
208     };
209 
WallpaperController(WindowManagerService service, DisplayContent displayContent)210     WallpaperController(WindowManagerService service, DisplayContent displayContent) {
211         mService = service;
212         mDisplayContent = displayContent;
213         mMaxWallpaperScale = service.mContext.getResources()
214                 .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
215     }
216 
getWallpaperTarget()217     WindowState getWallpaperTarget() {
218         return mWallpaperTarget;
219     }
220 
isWallpaperTarget(WindowState win)221     boolean isWallpaperTarget(WindowState win) {
222         return win == mWallpaperTarget;
223     }
224 
isBelowWallpaperTarget(WindowState win)225     boolean isBelowWallpaperTarget(WindowState win) {
226         return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer;
227     }
228 
isWallpaperVisible()229     boolean isWallpaperVisible() {
230         return isWallpaperVisible(mWallpaperTarget);
231     }
232 
233     /**
234      * Starts {@param a} on all wallpaper windows.
235      */
startWallpaperAnimation(Animation a)236     void startWallpaperAnimation(Animation a) {
237         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
238             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
239             token.startAnimation(a);
240         }
241     }
242 
isWallpaperVisible(WindowState wallpaperTarget)243     private boolean isWallpaperVisible(WindowState wallpaperTarget) {
244         if (DEBUG_WALLPAPER) {
245             Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + " prev="
246                     + mPrevWallpaperTarget);
247         }
248         return wallpaperTarget != null || mPrevWallpaperTarget != null;
249     }
250 
isWallpaperTargetAnimating()251     boolean isWallpaperTargetAnimating() {
252         return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
253                 && (mWallpaperTarget.mActivityRecord == null
254                         || !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart());
255     }
256 
updateWallpaperVisibility()257     void updateWallpaperVisibility() {
258         final boolean visible = isWallpaperVisible(mWallpaperTarget);
259 
260         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
261             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
262             token.updateWallpaperVisibility(visible);
263         }
264     }
265 
hideDeferredWallpapersIfNeeded()266     void hideDeferredWallpapersIfNeeded() {
267         if (mDeferredHideWallpaper != null) {
268             hideWallpapers(mDeferredHideWallpaper);
269             mDeferredHideWallpaper = null;
270         }
271     }
272 
hideWallpapers(final WindowState winGoingAway)273     void hideWallpapers(final WindowState winGoingAway) {
274         if (mWallpaperTarget != null
275                 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
276             return;
277         }
278         if (mWallpaperTarget != null
279                 && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) {
280             // Defer hiding the wallpaper when app transition is running until the animations
281             // are done.
282             mDeferredHideWallpaper = winGoingAway;
283             return;
284         }
285 
286         final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
287         for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
288             final WallpaperWindowToken token = mWallpaperTokens.get(i);
289             token.hideWallpaperToken(wasDeferred, "hideWallpapers");
290             if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
291                 Slog.d(TAG, "Hiding wallpaper " + token
292                         + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
293                         + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
294             }
295         }
296     }
297 
updateWallpaperOffset(WindowState wallpaperWin, boolean sync)298     boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
299         final DisplayInfo displayInfo = wallpaperWin.getDisplayInfo();
300         final int dw = displayInfo.logicalWidth;
301         final int dh = displayInfo.logicalHeight;
302 
303         int xOffset = 0;
304         int yOffset = 0;
305         boolean rawChanged = false;
306         // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
307         // match the behavior of most Launchers
308         float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
309         float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
310         float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
311         int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw;
312         int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
313         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
314             offset += mLastWallpaperDisplayOffsetX;
315         }
316         xOffset = offset;
317 
318         if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
319             wallpaperWin.mWallpaperX = wpx;
320             wallpaperWin.mWallpaperXStep = wpxs;
321             rawChanged = true;
322         }
323 
324         float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
325         float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
326         int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh;
327         offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
328         if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
329             offset += mLastWallpaperDisplayOffsetY;
330         }
331         yOffset = offset;
332 
333         if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
334             wallpaperWin.mWallpaperY = wpy;
335             wallpaperWin.mWallpaperYStep = wpys;
336             rawChanged = true;
337         }
338 
339         if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) {
340             wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut;
341             rawChanged = true;
342         }
343 
344         boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset,
345                 wallpaperWin.mShouldScaleWallpaper
346                         ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1);
347 
348         if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
349                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
350             try {
351                 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
352                         + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
353                         + " y=" + wallpaperWin.mWallpaperY
354                         + " zoom=" + wallpaperWin.mWallpaperZoomOut);
355                 if (sync) {
356                     mWaitingOnWallpaper = wallpaperWin;
357                 }
358                 wallpaperWin.mClient.dispatchWallpaperOffsets(
359                         wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
360                         wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep,
361                         wallpaperWin.mWallpaperZoomOut, sync);
362 
363                 if (sync) {
364                     if (mWaitingOnWallpaper != null) {
365                         long start = SystemClock.uptimeMillis();
366                         if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY)
367                                 < start) {
368                             try {
369                                 if (DEBUG_WALLPAPER) Slog.v(TAG,
370                                         "Waiting for offset complete...");
371                                 mService.mGlobalLock.wait(WALLPAPER_TIMEOUT);
372                             } catch (InterruptedException e) {
373                             }
374                             if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!");
375                             if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) {
376                                 Slog.i(TAG, "Timeout waiting for wallpaper to offset: "
377                                         + wallpaperWin);
378                                 mLastWallpaperTimeoutTime = start;
379                             }
380                         }
381                         mWaitingOnWallpaper = null;
382                     }
383                 }
384             } catch (RemoteException e) {
385             }
386         }
387 
388         return changed;
389     }
390 
setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)391     void setWindowWallpaperPosition(
392             WindowState window, float x, float y, float xStep, float yStep) {
393         if (window.mWallpaperX != x || window.mWallpaperY != y)  {
394             window.mWallpaperX = x;
395             window.mWallpaperY = y;
396             window.mWallpaperXStep = xStep;
397             window.mWallpaperYStep = yStep;
398             updateWallpaperOffsetLocked(window, true);
399         }
400     }
401 
setWallpaperZoomOut(WindowState window, float zoom)402     void setWallpaperZoomOut(WindowState window, float zoom) {
403         if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
404             window.mWallpaperZoomOut = zoom;
405             mShouldUpdateZoom = true;
406             updateWallpaperOffsetLocked(window, false);
407         }
408     }
409 
setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom)410     void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) {
411         if (shouldZoom != window.mShouldScaleWallpaper) {
412             window.mShouldScaleWallpaper = shouldZoom;
413             updateWallpaperOffsetLocked(window, false);
414         }
415     }
416 
setWindowWallpaperDisplayOffset(WindowState window, int x, int y)417     void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
418         if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y)  {
419             window.mWallpaperDisplayOffsetX = x;
420             window.mWallpaperDisplayOffsetY = y;
421             updateWallpaperOffsetLocked(window, true);
422         }
423     }
424 
sendWindowWallpaperCommand( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)425     Bundle sendWindowWallpaperCommand(
426             WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
427         if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
428             boolean doWait = sync;
429             for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
430                 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
431                 token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
432             }
433 
434             if (doWait) {
435                 // TODO: Need to wait for result.
436             }
437         }
438 
439         return null;
440     }
441 
updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)442     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
443         WindowState target = mWallpaperTarget;
444         if (target != null) {
445             if (target.mWallpaperX >= 0) {
446                 mLastWallpaperX = target.mWallpaperX;
447             } else if (changingTarget.mWallpaperX >= 0) {
448                 mLastWallpaperX = changingTarget.mWallpaperX;
449             }
450             if (target.mWallpaperY >= 0) {
451                 mLastWallpaperY = target.mWallpaperY;
452             } else if (changingTarget.mWallpaperY >= 0) {
453                 mLastWallpaperY = changingTarget.mWallpaperY;
454             }
455             computeLastWallpaperZoomOut();
456             if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
457                 mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
458             } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
459                 mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
460             }
461             if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
462                 mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
463             } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
464                 mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
465             }
466             if (target.mWallpaperXStep >= 0) {
467                 mLastWallpaperXStep = target.mWallpaperXStep;
468             } else if (changingTarget.mWallpaperXStep >= 0) {
469                 mLastWallpaperXStep = changingTarget.mWallpaperXStep;
470             }
471             if (target.mWallpaperYStep >= 0) {
472                 mLastWallpaperYStep = target.mWallpaperYStep;
473             } else if (changingTarget.mWallpaperYStep >= 0) {
474                 mLastWallpaperYStep = changingTarget.mWallpaperYStep;
475             }
476         }
477 
478         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
479             mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(sync);
480         }
481     }
482 
clearLastWallpaperTimeoutTime()483     void clearLastWallpaperTimeoutTime() {
484         mLastWallpaperTimeoutTime = 0;
485     }
486 
wallpaperCommandComplete(IBinder window)487     void wallpaperCommandComplete(IBinder window) {
488         if (mWaitingOnWallpaper != null &&
489                 mWaitingOnWallpaper.mClient.asBinder() == window) {
490             mWaitingOnWallpaper = null;
491             mService.mGlobalLock.notifyAll();
492         }
493     }
494 
wallpaperOffsetsComplete(IBinder window)495     void wallpaperOffsetsComplete(IBinder window) {
496         if (mWaitingOnWallpaper != null &&
497                 mWaitingOnWallpaper.mClient.asBinder() == window) {
498             mWaitingOnWallpaper = null;
499             mService.mGlobalLock.notifyAll();
500         }
501     }
502 
findWallpaperTarget()503     private void findWallpaperTarget() {
504         mFindResults.reset();
505         if (mDisplayContent.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)) {
506             // In freeform mode we set the wallpaper as its own target, so we don't need an
507             // additional window to make it visible.
508             mFindResults.setUseTopWallpaperAsTarget(true);
509         }
510 
511         mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
512 
513         if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
514             mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
515         }
516     }
517 
isFullscreen(WindowManager.LayoutParams attrs)518     private boolean isFullscreen(WindowManager.LayoutParams attrs) {
519         return attrs.x == 0 && attrs.y == 0
520                 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT;
521     }
522 
523     /** Updates the target wallpaper if needed and returns true if an update happened. */
updateWallpaperWindowsTarget(FindWallpaperTargetResult result)524     private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) {
525 
526         WindowState wallpaperTarget = result.wallpaperTarget;
527 
528         if (mWallpaperTarget == wallpaperTarget
529                 || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) {
530 
531             if (mPrevWallpaperTarget == null) {
532                 return;
533             }
534 
535             // Is it time to stop animating?
536             if (!mPrevWallpaperTarget.isAnimatingLw()) {
537                 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
538                 mPrevWallpaperTarget = null;
539                 mWallpaperTarget = wallpaperTarget;
540             }
541             return;
542         }
543 
544         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
545                 "New wallpaper target: " + wallpaperTarget + " prevTarget: " + mWallpaperTarget);
546 
547         mPrevWallpaperTarget = null;
548 
549         final WindowState prevWallpaperTarget = mWallpaperTarget;
550         mWallpaperTarget = wallpaperTarget;
551 
552         if (wallpaperTarget == null || prevWallpaperTarget == null) {
553             return;
554         }
555 
556         // Now what is happening...  if the current and new targets are animating,
557         // then we are in our super special mode!
558         boolean oldAnim = prevWallpaperTarget.isAnimatingLw();
559         boolean foundAnim = wallpaperTarget.isAnimatingLw();
560         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
561                 "New animation: " + foundAnim + " old animation: " + oldAnim);
562 
563         if (!foundAnim || !oldAnim) {
564             return;
565         }
566 
567         if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) {
568             return;
569         }
570 
571         final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null
572                 && !wallpaperTarget.mActivityRecord.mVisibleRequested;
573         final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
574                 && !prevWallpaperTarget.mActivityRecord.mVisibleRequested;
575 
576         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
577                 + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
578                 + " hidden=" + newTargetHidden);
579 
580         mPrevWallpaperTarget = prevWallpaperTarget;
581 
582         if (newTargetHidden && !oldTargetHidden) {
583             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target.");
584             // Use the old target if new target is hidden but old target
585             // is not. If they're both hidden, still use the new target.
586             mWallpaperTarget = prevWallpaperTarget;
587         } else if (newTargetHidden == oldTargetHidden
588                 && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mActivityRecord)
589                 && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mActivityRecord)
590                 || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mActivityRecord))) {
591             // If they're both hidden (or both not hidden), prefer the one that's currently in
592             // opening or closing app list, this allows transition selection logic to better
593             // determine the wallpaper status of opening/closing apps.
594             mWallpaperTarget = prevWallpaperTarget;
595         }
596 
597         result.setWallpaperTarget(wallpaperTarget);
598     }
599 
updateWallpaperTokens(boolean visible)600     private void updateWallpaperTokens(boolean visible) {
601         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
602             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
603             token.updateWallpaperWindows(visible);
604             token.getDisplayContent().assignWindowLayers(false);
605         }
606     }
607 
adjustWallpaperWindows()608     void adjustWallpaperWindows() {
609         mDisplayContent.mWallpaperMayChange = false;
610 
611         // First find top-most window that has asked to be on top of the wallpaper;
612         // all wallpapers go behind it.
613         findWallpaperTarget();
614         updateWallpaperWindowsTarget(mFindResults);
615 
616         // The window is visible to the compositor...but is it visible to the user?
617         // That is what the wallpaper cares about.
618         final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
619         if (DEBUG_WALLPAPER) {
620             Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
621                     + mDisplayContent.getDisplayId());
622         }
623 
624         if (visible) {
625             if (mWallpaperTarget.mWallpaperX >= 0) {
626                 mLastWallpaperX = mWallpaperTarget.mWallpaperX;
627                 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
628             }
629             computeLastWallpaperZoomOut();
630             if (mWallpaperTarget.mWallpaperY >= 0) {
631                 mLastWallpaperY = mWallpaperTarget.mWallpaperY;
632                 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
633             }
634             if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
635                 mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
636             }
637             if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
638                 mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
639             }
640         }
641 
642         updateWallpaperTokens(visible);
643 
644         if (DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
645                 + " prev=" + mPrevWallpaperTarget);
646     }
647 
processWallpaperDrawPendingTimeout()648     boolean processWallpaperDrawPendingTimeout() {
649         if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
650             mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
651             if (DEBUG_WALLPAPER) {
652                 Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT");
653             }
654 
655             // If there was a pending recents animation, start the animation anyways (it's better
656             // to not see the wallpaper than for the animation to not start)
657             if (mService.getRecentsAnimationController() != null) {
658                 mService.getRecentsAnimationController().startAnimation();
659             }
660             return true;
661         }
662         return false;
663     }
664 
wallpaperTransitionReady()665     boolean wallpaperTransitionReady() {
666         boolean transitionReady = true;
667         boolean wallpaperReady = true;
668         for (int curTokenIndex = mWallpaperTokens.size() - 1;
669                 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
670             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex);
671             if (token.hasVisibleNotDrawnWallpaper()) {
672                 // We've told this wallpaper to be visible, but it is not drawn yet
673                 wallpaperReady = false;
674                 if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
675                     // wait for this wallpaper until it is drawn or timeout
676                     transitionReady = false;
677                 }
678                 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
679                     mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
680                     mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
681                     mService.mH.sendMessageDelayed(
682                                 mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this),
683                                 WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
684 
685                 }
686                 if (DEBUG_WALLPAPER) {
687                     Slog.v(TAG,
688                             "Wallpaper should be visible but has not been drawn yet. "
689                                     + "mWallpaperDrawState=" + mWallpaperDrawState);
690                 }
691                 break;
692             }
693         }
694         if (wallpaperReady) {
695             mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
696             mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
697         }
698 
699         return transitionReady;
700     }
701 
702     /**
703      * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
704      * the opening apps should be a wallpaper target.
705      */
adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps)706     void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) {
707         boolean adjust = false;
708         if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
709             adjust = true;
710         } else {
711             for (int i = openingApps.size() - 1; i >= 0; --i) {
712                 final ActivityRecord activity = openingApps.valueAt(i);
713                 if (activity.windowsCanBeWallpaperTarget()) {
714                     adjust = true;
715                     break;
716                 }
717             }
718         }
719 
720         if (adjust) {
721             adjustWallpaperWindows();
722         }
723     }
724 
addWallpaperToken(WallpaperWindowToken token)725     void addWallpaperToken(WallpaperWindowToken token) {
726         mWallpaperTokens.add(token);
727     }
728 
removeWallpaperToken(WallpaperWindowToken token)729     void removeWallpaperToken(WallpaperWindowToken token) {
730         mWallpaperTokens.remove(token);
731     }
732 
733 
734     @VisibleForTesting
canScreenshotWallpaper()735     boolean canScreenshotWallpaper() {
736         return canScreenshotWallpaper(getTopVisibleWallpaper());
737     }
738 
canScreenshotWallpaper(WindowState wallpaperWindowState)739     private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) {
740         if (!mService.mPolicy.isScreenOn()) {
741             if (DEBUG_SCREENSHOT) {
742                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
743             }
744             return false;
745         }
746 
747         if (wallpaperWindowState == null) {
748             if (DEBUG_SCREENSHOT) {
749                 Slog.i(TAG_WM, "No visible wallpaper to screenshot");
750             }
751             return false;
752         }
753         return true;
754     }
755 
756     /**
757      * Take a screenshot of the wallpaper if it's visible.
758      *
759      * @return Bitmap of the wallpaper
760      */
screenshotWallpaperLocked()761     Bitmap screenshotWallpaperLocked() {
762         final WindowState wallpaperWindowState = getTopVisibleWallpaper();
763         if (!canScreenshotWallpaper(wallpaperWindowState)) {
764             return null;
765         }
766 
767         final Rect bounds = wallpaperWindowState.getBounds();
768         bounds.offsetTo(0, 0);
769 
770         SurfaceControl.ScreenshotGraphicBuffer wallpaperBuffer = SurfaceControl.captureLayers(
771                 wallpaperWindowState.getSurfaceControl(), bounds, 1 /* frameScale */);
772 
773         if (wallpaperBuffer == null) {
774             Slog.w(TAG_WM, "Failed to screenshot wallpaper");
775             return null;
776         }
777         return Bitmap.wrapHardwareBuffer(
778                 wallpaperBuffer.getGraphicBuffer(), wallpaperBuffer.getColorSpace());
779     }
780 
getTopVisibleWallpaper()781     private WindowState getTopVisibleWallpaper() {
782         mTmpTopWallpaper = null;
783 
784         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
785             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
786             token.forAllWindows(w -> {
787                 final WindowStateAnimator winAnim = w.mWinAnimator;
788                 if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
789                     mTmpTopWallpaper = w;
790                     return true;
791                 }
792                 return false;
793             }, true /* traverseTopToBottom */);
794         }
795 
796         return mTmpTopWallpaper;
797     }
798 
799     /**
800      * Each window can request a zoom, example:
801      * - User is in overview, zoomed out.
802      * - User also pulls down the shade.
803      *
804      * This means that we always have to choose the largest zoom out that we have, otherwise
805      * we'll have conflicts and break the "depth system" mental model.
806      */
computeLastWallpaperZoomOut()807     private void computeLastWallpaperZoomOut() {
808         if (mShouldUpdateZoom) {
809             mLastWallpaperZoomOut = 0;
810             mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
811             mShouldUpdateZoom = false;
812         }
813     }
814 
zoomOutToScale(float zoom)815     private float zoomOutToScale(float zoom) {
816         return MathUtils.lerp(1, mMaxWallpaperScale, 1 - zoom);
817     }
818 
dump(PrintWriter pw, String prefix)819     void dump(PrintWriter pw, String prefix) {
820         pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
821         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
822         if (mPrevWallpaperTarget != null) {
823             pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
824         }
825         pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
826         pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
827         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
828                 || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
829             pw.print(prefix);
830             pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
831             pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
832         }
833     }
834 
835     /** Helper class for storing the results of a wallpaper target find operation. */
836     final private static class FindWallpaperTargetResult {
837         WindowState topWallpaper = null;
838         boolean useTopWallpaperAsTarget = false;
839         WindowState wallpaperTarget = null;
840         boolean resetTopWallpaper = false;
841 
setTopWallpaper(WindowState win)842         void setTopWallpaper(WindowState win) {
843             topWallpaper = win;
844         }
845 
setWallpaperTarget(WindowState win)846         void setWallpaperTarget(WindowState win) {
847             wallpaperTarget = win;
848         }
849 
setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget)850         void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) {
851             useTopWallpaperAsTarget = topWallpaperAsTarget;
852         }
853 
reset()854         void reset() {
855             topWallpaper = null;
856             wallpaperTarget = null;
857             useTopWallpaperAsTarget = false;
858             resetTopWallpaper = false;
859         }
860     }
861 }
862