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