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