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