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