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