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