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