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