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