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