WallpaperController.java revision 51cb83e1db4d84ced550a41e172d523ec6614c0a
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.WindowManagerService.DEBUG_ADD_REMOVE;
26import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
27import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
28import static com.android.server.wm.WindowManagerService.DEBUG_WINDOW_MOVEMENT;
29import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
30import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER;
31import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER_LIGHT;
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.Slog;
42import android.view.DisplayInfo;
43import android.view.WindowManager;
44import android.view.WindowManagerPolicy;
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 = com.android.server.wm.WindowManagerService.TAG;
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    private 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.isAnimating()
148                && !mWallpaperTarget.mWinAnimator.isDummyAnimation();
149    }
150
151    void updateWallpaperVisibility() {
152        final boolean visible = isWallpaperVisible(mWallpaperTarget);
153        final DisplayContent displayContent = mWallpaperTarget.getDisplayContent();
154        if (displayContent == null) {
155            return;
156        }
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            WindowToken token = mWallpaperTokens.get(curTokenNdx);
163            if (token.hidden == visible) {
164                token.hidden = !visible;
165                // Need to do a layout to ensure the wallpaper now has the
166                // correct size.
167                displayContent.layoutNeeded = true;
168            }
169
170            final WindowList windows = token.windows;
171            for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
172                WindowState wallpaper = windows.get(wallpaperNdx);
173                if (visible) {
174                    updateWallpaperOffset(wallpaper, dw, dh, false);
175                }
176
177                dispatchWallpaperVisibility(wallpaper, visible);
178            }
179        }
180    }
181
182    void hideDeferredWallpapersIfNeeded() {
183        if (mDeferredHideWallpaper != null) {
184            hideWallpapers(mDeferredHideWallpaper);
185            mDeferredHideWallpaper = null;
186        }
187    }
188
189    void hideWallpapers(final WindowState winGoingAway) {
190        if (mWallpaperTarget != null
191                && (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
192            return;
193        }
194        if (mService.mAppTransition.isRunning()) {
195            // Defer hiding the wallpaper when app transition is running until the animations
196            // are done.
197            mDeferredHideWallpaper = winGoingAway;
198            return;
199        }
200
201        final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
202        for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
203            final WindowToken token = mWallpaperTokens.get(i);
204            for (int j = token.windows.size() - 1; j >= 0; j--) {
205                final WindowState wallpaper = token.windows.get(j);
206                final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
207                if (!winAnimator.mLastHidden || wasDeferred) {
208                    winAnimator.hide();
209                    dispatchWallpaperVisibility(wallpaper, false);
210                    final DisplayContent displayContent = wallpaper.getDisplayContent();
211                    if (displayContent != null) {
212                        displayContent.pendingLayoutChanges |=
213                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
214                    }
215                }
216            }
217            if (DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG, "Hiding wallpaper " + token
218                    + " from " + winGoingAway + " target=" + mWallpaperTarget + " lower="
219                    + mLowerWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
220            token.hidden = true;
221        }
222    }
223
224    /**
225     * Check wallpaper for visibility change and notify window if so.
226     * @param wallpaper The wallpaper to test and notify.
227     * @param visible Current visibility.
228     */
229    void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
230        // Only send notification if the visibility actually changed and we are not trying to hide
231        // the wallpaper when we are deferring hiding of the wallpaper.
232        if (wallpaper.mWallpaperVisible != visible
233                && (mDeferredHideWallpaper == null || visible)) {
234            wallpaper.mWallpaperVisible = visible;
235            try {
236                if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
237                        "Updating vis of wallpaper " + wallpaper
238                                + ": " + visible + " from:\n" + Debug.getCallers(4, "  "));
239                wallpaper.mClient.dispatchAppVisibility(visible);
240            } catch (RemoteException e) {
241            }
242        }
243    }
244
245    boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
246        boolean rawChanged = false;
247        float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
248        float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
249        int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
250        int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
251        if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
252            offset += mLastWallpaperDisplayOffsetX;
253        }
254        boolean changed = wallpaperWin.mXOffset != offset;
255        if (changed) {
256            if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
257            wallpaperWin.mXOffset = offset;
258        }
259        if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
260            wallpaperWin.mWallpaperX = wpx;
261            wallpaperWin.mWallpaperXStep = wpxs;
262            rawChanged = true;
263        }
264
265        float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
266        float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
267        int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
268        offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
269        if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
270            offset += mLastWallpaperDisplayOffsetY;
271        }
272        if (wallpaperWin.mYOffset != offset) {
273            if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
274            changed = true;
275            wallpaperWin.mYOffset = offset;
276        }
277        if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
278            wallpaperWin.mWallpaperY = wpy;
279            wallpaperWin.mWallpaperYStep = wpys;
280            rawChanged = true;
281        }
282
283        if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
284                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
285            try {
286                if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
287                        + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
288                        + " y=" + wallpaperWin.mWallpaperY);
289                if (sync) {
290                    mWaitingOnWallpaper = wallpaperWin;
291                }
292                wallpaperWin.mClient.dispatchWallpaperOffsets(
293                        wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
294                        wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
295                if (sync) {
296                    if (mWaitingOnWallpaper != null) {
297                        long start = SystemClock.uptimeMillis();
298                        if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY)
299                                < start) {
300                            try {
301                                if (DEBUG_WALLPAPER) Slog.v(TAG,
302                                        "Waiting for offset complete...");
303                                mService.mWindowMap.wait(WALLPAPER_TIMEOUT);
304                            } catch (InterruptedException e) {
305                            }
306                            if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!");
307                            if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) {
308                                Slog.i(TAG, "Timeout waiting for wallpaper to offset: "
309                                        + wallpaperWin);
310                                mLastWallpaperTimeoutTime = start;
311                            }
312                        }
313                        mWaitingOnWallpaper = null;
314                    }
315                }
316            } catch (RemoteException e) {
317            }
318        }
319
320        return changed;
321    }
322
323    void setWindowWallpaperPosition(
324            WindowState window, float x, float y, float xStep, float yStep) {
325        if (window.mWallpaperX != x || window.mWallpaperY != y)  {
326            window.mWallpaperX = x;
327            window.mWallpaperY = y;
328            window.mWallpaperXStep = xStep;
329            window.mWallpaperYStep = yStep;
330            updateWallpaperOffsetLocked(window, true);
331        }
332    }
333
334    void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
335        if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y)  {
336            window.mWallpaperDisplayOffsetX = x;
337            window.mWallpaperDisplayOffsetY = y;
338            updateWallpaperOffsetLocked(window, true);
339        }
340    }
341
342    Bundle sendWindowWallpaperCommand(
343            WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
344        if (window == mWallpaperTarget
345                || window == mLowerWallpaperTarget
346                || window == mUpperWallpaperTarget) {
347            boolean doWait = sync;
348            for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
349                final WindowList windows = mWallpaperTokens.get(curTokenNdx).windows;
350                for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
351                    WindowState wallpaper = windows.get(wallpaperNdx);
352                    try {
353                        wallpaper.mClient.dispatchWallpaperCommand(action,
354                                x, y, z, extras, sync);
355                        // We only want to be synchronous with one wallpaper.
356                        sync = false;
357                    } catch (RemoteException e) {
358                    }
359                }
360            }
361
362            if (doWait) {
363                // TODO: Need to wait for result.
364            }
365        }
366
367        return null;
368    }
369
370    void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
371        final DisplayContent displayContent = changingTarget.getDisplayContent();
372        if (displayContent == null) {
373            return;
374        }
375        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
376        final int dw = displayInfo.logicalWidth;
377        final int dh = displayInfo.logicalHeight;
378
379        WindowState target = mWallpaperTarget;
380        if (target != null) {
381            if (target.mWallpaperX >= 0) {
382                mLastWallpaperX = target.mWallpaperX;
383            } else if (changingTarget.mWallpaperX >= 0) {
384                mLastWallpaperX = changingTarget.mWallpaperX;
385            }
386            if (target.mWallpaperY >= 0) {
387                mLastWallpaperY = target.mWallpaperY;
388            } else if (changingTarget.mWallpaperY >= 0) {
389                mLastWallpaperY = changingTarget.mWallpaperY;
390            }
391            if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
392                mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
393            } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
394                mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
395            }
396            if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
397                mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
398            } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
399                mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
400            }
401            if (target.mWallpaperXStep >= 0) {
402                mLastWallpaperXStep = target.mWallpaperXStep;
403            } else if (changingTarget.mWallpaperXStep >= 0) {
404                mLastWallpaperXStep = changingTarget.mWallpaperXStep;
405            }
406            if (target.mWallpaperYStep >= 0) {
407                mLastWallpaperYStep = target.mWallpaperYStep;
408            } else if (changingTarget.mWallpaperYStep >= 0) {
409                mLastWallpaperYStep = changingTarget.mWallpaperYStep;
410            }
411        }
412
413        for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
414            WindowList windows = mWallpaperTokens.get(curTokenNdx).windows;
415            for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
416                WindowState wallpaper = windows.get(wallpaperNdx);
417                if (updateWallpaperOffset(wallpaper, dw, dh, sync)) {
418                    WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
419                    winAnimator.computeShownFrameLocked();
420                    // No need to lay out the windows - we can just set the wallpaper position
421                    // directly.
422                    winAnimator.setWallpaperOffset(wallpaper.mShownPosition);
423                    // We only want to be synchronous with one wallpaper.
424                    sync = false;
425                }
426            }
427        }
428    }
429
430    void clearLastWallpaperTimeoutTime() {
431        mLastWallpaperTimeoutTime = 0;
432    }
433
434    void wallpaperCommandComplete(IBinder window) {
435        if (mWaitingOnWallpaper != null &&
436                mWaitingOnWallpaper.mClient.asBinder() == window) {
437            mWaitingOnWallpaper = null;
438            mService.mWindowMap.notifyAll();
439        }
440    }
441
442    void wallpaperOffsetsComplete(IBinder window) {
443        if (mWaitingOnWallpaper != null &&
444                mWaitingOnWallpaper.mClient.asBinder() == window) {
445            mWaitingOnWallpaper = null;
446            mService.mWindowMap.notifyAll();
447        }
448    }
449
450    int getAnimLayerAdjustment() {
451        return mWallpaperAnimLayerAdjustment;
452    }
453
454    void setAnimLayerAdjustment(WindowState win, int adj) {
455        if (win != mWallpaperTarget || mLowerWallpaperTarget != null) {
456            return;
457        }
458
459        if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Setting wallpaper layer adj to " + adj);
460        mWallpaperAnimLayerAdjustment = adj;
461        for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
462            WindowList windows = mWallpaperTokens.get(i).windows;
463            for (int j = windows.size() - 1; j >= 0; j--) {
464                WindowState wallpaper = windows.get(j);
465                wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + adj;
466                if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "setWallpaper win "
467                        + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
468            }
469        }
470    }
471
472    private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
473        final WindowAnimator winAnimator = mService.mAnimator;
474        result.reset();
475        WindowState w = null;
476        int windowDetachedI = -1;
477        boolean resetTopWallpaper = false;
478        boolean inFreeformSpace = false;
479        boolean replacing = false;
480        for (int i = windows.size() - 1; i >= 0; i--) {
481            w = windows.get(i);
482            if ((w.mAttrs.type == TYPE_WALLPAPER)) {
483                if (result.topWallpaper == null || resetTopWallpaper) {
484                    result.setTopWallpaper(w, i);
485                    resetTopWallpaper = false;
486                }
487                continue;
488            }
489            resetTopWallpaper = true;
490            if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
491                // If this window's app token is hidden and not animating,
492                // it is of no interest to us.
493                if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
494                    if (DEBUG_WALLPAPER) Slog.v(TAG,
495                            "Skipping hidden and not animating token: " + w);
496                    continue;
497                }
498            }
499            if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen="
500                    + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState);
501
502            if (!inFreeformSpace) {
503                TaskStack stack = w.getStack();
504                inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
505            }
506
507            replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
508
509            // If the app is executing an animation because the keyguard is going away,
510            // keep the wallpaper during the animation so it doesn't flicker out.
511            final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
512                    || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation);
513            if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
514                if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
515                result.setWallpaperTarget(w, i);
516                if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) {
517                    // The current wallpaper target is animating, so we'll look behind it for
518                    // another possible target and figure out what is going on later.
519                    if (DEBUG_WALLPAPER) Slog.v(TAG,
520                            "Win " + w + ": token animating, looking behind.");
521                    continue;
522                }
523                break;
524            } else if (w == winAnimator.mWindowDetachedWallpaper) {
525                windowDetachedI = i;
526            }
527        }
528
529        if (result.wallpaperTarget == null && windowDetachedI >= 0) {
530            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
531                    "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w);
532            result.setWallpaperTarget(w, windowDetachedI);
533        }
534        if (result.wallpaperTarget == null
535                && (inFreeformSpace || (replacing && mWallpaperTarget != null))) {
536            // In freeform mode we set the wallpaper as its own target, so we don't need an
537            // additional window to make it visible. When we are replacing a window and there was
538            // wallpaper before replacement, we want to keep the window until the new windows fully
539            // appear and can determine the visibility, to avoid flickering.
540            result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
541        }
542    }
543
544    private boolean updateWallpaperWindowsTarget(
545            WindowList windows, FindWallpaperTargetResult result) {
546
547        boolean targetChanged = false;
548        WindowState wallpaperTarget = result.wallpaperTarget;
549        int wallpaperTargetIndex = result.wallpaperTargetIndex;
550
551        if (mWallpaperTarget != wallpaperTarget
552                && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != wallpaperTarget)) {
553            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
554                    "New wallpaper target: " + wallpaperTarget + " oldTarget: " + mWallpaperTarget);
555
556            mLowerWallpaperTarget = null;
557            mUpperWallpaperTarget = null;
558
559            WindowState oldW = mWallpaperTarget;
560            mWallpaperTarget = wallpaperTarget;
561            targetChanged = true;
562
563            // Now what is happening...  if the current and new targets are animating,
564            // then we are in our super special mode!
565            if (wallpaperTarget != null && oldW != null) {
566                boolean oldAnim = oldW.isAnimatingLw();
567                boolean foundAnim = wallpaperTarget.isAnimatingLw();
568                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
569                        "New animation: " + foundAnim + " old animation: " + oldAnim);
570                if (foundAnim && oldAnim) {
571                    int oldI = windows.indexOf(oldW);
572                    if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
573                            "New i: " + wallpaperTargetIndex + " old i: " + oldI);
574                    if (oldI >= 0) {
575                        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
576                                "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#"
577                                + wallpaperTargetIndex + "=" + wallpaperTarget);
578
579                        // Set the new target correctly.
580                        if (wallpaperTarget.mAppToken != null
581                                && wallpaperTarget.mAppToken.hiddenRequested) {
582                            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
583                                    "Old wallpaper still the target.");
584                            mWallpaperTarget = oldW;
585                            wallpaperTarget = oldW;
586                            wallpaperTargetIndex = oldI;
587                        }
588                        // Now set the upper and lower wallpaper targets correctly,
589                        // and make sure that we are positioning the wallpaper below the lower.
590                        else if (wallpaperTargetIndex > oldI) {
591                            // The new target is on top of the old one.
592                            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
593                                    "Found target above old target.");
594                            mUpperWallpaperTarget = wallpaperTarget;
595                            mLowerWallpaperTarget = oldW;
596                            wallpaperTarget = oldW;
597                            wallpaperTargetIndex = oldI;
598                        } else {
599                            // The new target is below the old one.
600                            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
601                                    "Found target below old target.");
602                            mUpperWallpaperTarget = oldW;
603                            mLowerWallpaperTarget = wallpaperTarget;
604                        }
605                    }
606                }
607            }
608
609        } else if (mLowerWallpaperTarget != null) {
610            // Is it time to stop animating?
611            if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) {
612                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
613                mLowerWallpaperTarget = null;
614                mUpperWallpaperTarget = null;
615                mWallpaperTarget = wallpaperTarget;
616                targetChanged = true;
617            }
618        }
619
620        result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
621        return targetChanged;
622    }
623
624    boolean updateWallpaperWindowsTargetByLayer(
625            WindowList windows, FindWallpaperTargetResult result) {
626
627        WindowState wallpaperTarget = result.wallpaperTarget;
628        int wallpaperTargetIndex = result.wallpaperTargetIndex;
629        boolean visible = wallpaperTarget != null;
630
631        if (visible) {
632            // The window is visible to the compositor...but is it visible to the user?
633            // That is what the wallpaper cares about.
634            visible = isWallpaperVisible(wallpaperTarget);
635            if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
636
637            // If the wallpaper target is animating, we may need to copy its layer adjustment.
638            // Only do this if we are not transferring between two wallpaper targets.
639            mWallpaperAnimLayerAdjustment =
640                    (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null)
641                            ? wallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0;
642
643            final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER)
644                    + TYPE_LAYER_OFFSET;
645
646            // Now w is the window we are supposed to be behind...  but we
647            // need to be sure to also be behind any of its attached windows,
648            // AND any starting window associated with it, AND below the
649            // maximum layer the policy allows for wallpapers.
650            while (wallpaperTargetIndex > 0) {
651                WindowState wb = windows.get(wallpaperTargetIndex - 1);
652                if (wb.mBaseLayer < maxLayer &&
653                        wb.mAttachedWindow != wallpaperTarget &&
654                        (wallpaperTarget.mAttachedWindow == null ||
655                                wb.mAttachedWindow != wallpaperTarget.mAttachedWindow) &&
656                        (wb.mAttrs.type != TYPE_APPLICATION_STARTING
657                                || wallpaperTarget.mToken == null
658                                || wb.mToken != wallpaperTarget.mToken)) {
659                    // This window is not related to the previous one in any
660                    // interesting way, so stop here.
661                    break;
662                }
663                wallpaperTarget = wb;
664                wallpaperTargetIndex--;
665            }
666        } else {
667            if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target");
668        }
669
670        result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
671        return visible;
672    }
673
674    boolean updateWallpaperWindowsPlacement(WindowList windows,
675            WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) {
676
677        // TODO(multidisplay): Wallpapers on main screen only.
678        final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo();
679        final int dw = displayInfo.logicalWidth;
680        final int dh = displayInfo.logicalHeight;
681
682        // Start stepping backwards from here, ensuring that our wallpaper windows
683        // are correctly placed.
684        boolean changed = false;
685        for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
686            WindowToken token = mWallpaperTokens.get(curTokenNdx);
687            if (token.hidden == visible) {
688                if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
689                        "Wallpaper token " + token + " hidden=" + !visible);
690                token.hidden = !visible;
691                // Need to do a layout to ensure the wallpaper now has the correct size.
692                mService.getDefaultDisplayContentLocked().layoutNeeded = true;
693            }
694
695            final WindowList tokenWindows = token.windows;
696            for (int wallpaperNdx = tokenWindows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
697                WindowState wallpaper = tokenWindows.get(wallpaperNdx);
698
699                if (visible) {
700                    updateWallpaperOffset(wallpaper, dw, dh, false);
701                }
702
703                // First, make sure the client has the current visibility state.
704                dispatchWallpaperVisibility(wallpaper, visible);
705
706                wallpaper.mWinAnimator.mAnimLayer =
707                        wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
708                if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
709                        + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
710
711                // First, if this window is at the current index, then all is well.
712                if (wallpaper == wallpaperTarget) {
713                    wallpaperTargetIndex--;
714                    wallpaperTarget = wallpaperTargetIndex > 0
715                            ? windows.get(wallpaperTargetIndex - 1) : null;
716                    continue;
717                }
718
719                // The window didn't match...  the current wallpaper window,
720                // wherever it is, is in the wrong place, so make sure it is not in the list.
721                int oldIndex = windows.indexOf(wallpaper);
722                if (oldIndex >= 0) {
723                    if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
724                            "Wallpaper removing at " + oldIndex + ": " + wallpaper);
725                    windows.remove(oldIndex);
726                    mService.mWindowsChanged = true;
727                    if (oldIndex < wallpaperTargetIndex) {
728                        wallpaperTargetIndex--;
729                    }
730                }
731
732                // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
733                // layer. For keyguard over wallpaper put the wallpaper under the keyguard.
734                int insertionIndex = 0;
735                if (visible && wallpaperTarget != null) {
736                    final int type = wallpaperTarget.mAttrs.type;
737                    final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
738                    if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0
739                            || type == TYPE_KEYGUARD_SCRIM) {
740                        insertionIndex = windows.indexOf(wallpaperTarget);
741                    }
742                }
743                if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
744                        || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
745                        "Moving wallpaper " + wallpaper
746                        + " from " + oldIndex + " to " + insertionIndex);
747
748                windows.add(insertionIndex, wallpaper);
749                mService.mWindowsChanged = true;
750                changed = true;
751            }
752        }
753
754        return changed;
755    }
756
757    boolean adjustWallpaperWindows() {
758        mService.mWindowPlacerLocked.mWallpaperMayChange = false;
759
760        final WindowList windows = mService.getDefaultWindowListLocked();
761        // First find top-most window that has asked to be on top of the wallpaper;
762        // all wallpapers go behind it.
763        findWallpaperTarget(windows, mFindResults);
764        final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults);
765        final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults);
766        WindowState wallpaperTarget = mFindResults.wallpaperTarget;
767        int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex;
768
769        if (wallpaperTarget == null && mFindResults.topWallpaper != null) {
770            // There is no wallpaper target, so it goes at the bottom.
771            // We will assume it is the same place as last time, if known.
772            wallpaperTarget = mFindResults.topWallpaper;
773            wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1;
774        } else {
775            // Okay i is the position immediately above the wallpaper.
776            // Look at what is below it for later.
777            wallpaperTarget = wallpaperTargetIndex > 0
778                    ? windows.get(wallpaperTargetIndex - 1) : null;
779        }
780
781        if (visible) {
782            if (mWallpaperTarget.mWallpaperX >= 0) {
783                mLastWallpaperX = mWallpaperTarget.mWallpaperX;
784                mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
785            }
786            if (mWallpaperTarget.mWallpaperY >= 0) {
787                mLastWallpaperY = mWallpaperTarget.mWallpaperY;
788                mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
789            }
790            if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
791                mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
792            }
793            if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
794                mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
795            }
796        }
797
798        final boolean changed = updateWallpaperWindowsPlacement(
799                windows, wallpaperTarget, wallpaperTargetIndex, visible);
800
801        if (targetChanged && DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target="
802                + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper="
803                + mUpperWallpaperTarget);
804
805        return changed;
806    }
807
808    boolean processWallpaperDrawPendingTimeout() {
809        if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
810            mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
811            if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
812                    "*** WALLPAPER DRAW TIMEOUT");
813            return true;
814        }
815        return false;
816    }
817
818    boolean wallpaperTransitionReady() {
819        boolean transitionReady = true;
820        boolean wallpaperReady = true;
821        for (int curTokenIndex = mWallpaperTokens.size() - 1;
822                curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
823            WindowToken token = mWallpaperTokens.get(curTokenIndex);
824            for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
825                    curWallpaperIndex--) {
826                WindowState wallpaper = token.windows.get(curWallpaperIndex);
827                if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
828                    // We've told this wallpaper to be visible, but it is not drawn yet
829                    wallpaperReady = false;
830                    if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
831                        // wait for this wallpaper until it is drawn or timeout
832                        transitionReady = false;
833                    }
834                    if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
835                        mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
836                        mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
837                        mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
838                                WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
839                    }
840                    if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
841                            "Wallpaper should be visible but has not been drawn yet. " +
842                                    "mWallpaperDrawState=" + mWallpaperDrawState);
843                    break;
844                }
845            }
846        }
847        if (wallpaperReady) {
848            mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
849            mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
850        }
851
852        return transitionReady;
853    }
854
855    void addWallpaperToken(WindowToken token) {
856        mWallpaperTokens.add(token);
857    }
858
859    void removeWallpaperToken(WindowToken token) {
860        mWallpaperTokens.remove(token);
861    }
862
863    void dump(PrintWriter pw, String prefix) {
864        pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
865        if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) {
866            pw.print(prefix); pw.print("mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
867            pw.print(prefix); pw.print("mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
868        }
869        pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
870        pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
871        if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
872                || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
873            pw.print(prefix);
874            pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
875            pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
876        }
877    }
878
879    void dumpTokens(PrintWriter pw, String prefix, boolean dumpAll) {
880        if (!mWallpaperTokens.isEmpty()) {
881            pw.println();
882            pw.print(prefix); pw.println("Wallpaper tokens:");
883            for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
884                WindowToken token = mWallpaperTokens.get(i);
885                pw.print(prefix); pw.print("Wallpaper #"); pw.print(i);
886                pw.print(' '); pw.print(token);
887                if (dumpAll) {
888                    pw.println(':');
889                    token.dump(pw, "    ");
890                } else {
891                    pw.println();
892                }
893            }
894        }
895    }
896
897    /** Helper class for storing the results of a wallpaper target find operation. */
898    final private static class FindWallpaperTargetResult {
899        int topWallpaperIndex = 0;
900        WindowState topWallpaper = null;
901        int wallpaperTargetIndex = 0;
902        WindowState wallpaperTarget = null;
903
904        void setTopWallpaper(WindowState win, int index) {
905            topWallpaper = win;
906            topWallpaperIndex = index;
907        }
908
909        void setWallpaperTarget(WindowState win, int index) {
910            wallpaperTarget = win;
911            wallpaperTargetIndex = index;
912        }
913
914        void reset() {
915            topWallpaperIndex = 0;
916            topWallpaper = null;
917            wallpaperTargetIndex = 0;
918            wallpaperTarget = null;
919        }
920    }
921}
922