• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
20 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
21 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
23 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
24 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
25 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
26 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
27 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
31 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
33 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
34 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
35 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
36 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
37 
38 import android.os.Bundle;
39 import android.os.Debug;
40 import android.os.IBinder;
41 import android.os.RemoteException;
42 import android.os.SystemClock;
43 import android.util.Slog;
44 import android.view.DisplayInfo;
45 import android.view.WindowManager;
46 import android.view.WindowManagerPolicy;
47 
48 import java.io.PrintWriter;
49 import 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  */
55 class 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 
WallpaperController(WindowManagerService service)106     public WallpaperController(WindowManagerService service) {
107         mService = service;
108     }
109 
getWallpaperTarget()110     WindowState getWallpaperTarget() {
111         return mWallpaperTarget;
112     }
113 
getLowerWallpaperTarget()114     WindowState getLowerWallpaperTarget() {
115         return mLowerWallpaperTarget;
116     }
117 
getUpperWallpaperTarget()118     WindowState getUpperWallpaperTarget() {
119         return mUpperWallpaperTarget;
120     }
121 
isWallpaperTarget(WindowState win)122     boolean isWallpaperTarget(WindowState win) {
123         return win == mWallpaperTarget;
124     }
125 
isBelowWallpaperTarget(WindowState win)126     boolean isBelowWallpaperTarget(WindowState win) {
127         return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer;
128     }
129 
isWallpaperVisible()130     boolean isWallpaperVisible() {
131         return isWallpaperVisible(mWallpaperTarget);
132     }
133 
isWallpaperVisible(WindowState wallpaperTarget)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 
isWallpaperTargetAnimating()148     boolean isWallpaperTargetAnimating() {
149         return mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimationSet()
150                 && !mWallpaperTarget.mWinAnimator.isDummyAnimation();
151     }
152 
updateWallpaperVisibility()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 
hideDeferredWallpapersIfNeeded()184     void hideDeferredWallpapersIfNeeded() {
185         if (mDeferredHideWallpaper != null) {
186             hideWallpapers(mDeferredHideWallpaper);
187             mDeferredHideWallpaper = null;
188         }
189     }
190 
hideWallpapers(final WindowState winGoingAway)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      */
dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible)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 
updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync)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 
setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)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 
setWindowWallpaperDisplayOffset(WindowState window, int x, int y)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 
sendWindowWallpaperCommand( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)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 
updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)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 
clearLastWallpaperTimeoutTime()435     void clearLastWallpaperTimeoutTime() {
436         mLastWallpaperTimeoutTime = 0;
437     }
438 
wallpaperCommandComplete(IBinder window)439     void wallpaperCommandComplete(IBinder window) {
440         if (mWaitingOnWallpaper != null &&
441                 mWaitingOnWallpaper.mClient.asBinder() == window) {
442             mWaitingOnWallpaper = null;
443             mService.mWindowMap.notifyAll();
444         }
445     }
446 
wallpaperOffsetsComplete(IBinder window)447     void wallpaperOffsetsComplete(IBinder window) {
448         if (mWaitingOnWallpaper != null &&
449                 mWaitingOnWallpaper.mClient.asBinder() == window) {
450             mWaitingOnWallpaper = null;
451             mService.mWindowMap.notifyAll();
452         }
453     }
454 
getAnimLayerAdjustment()455     int getAnimLayerAdjustment() {
456         return mWallpaperAnimLayerAdjustment;
457     }
458 
setAnimLayerAdjustment(WindowState win, int adj)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 
findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result)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 
updateWallpaperWindowsTarget( WindowList windows, FindWallpaperTargetResult result)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                         final boolean newTargetHidden =
590                                 wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.hiddenRequested;
591                         final boolean oldTargetHidden =
592                                 oldW.mAppToken != null && oldW.mAppToken.hiddenRequested;
593                         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:"
594                                 + " old#" + oldI + "=" + oldW + " hidden=" + oldTargetHidden
595                                 + " new#" + wallpaperTargetIndex + "=" + wallpaperTarget
596                                 + " hidden=" + newTargetHidden);
597 
598                         // Set the upper and lower wallpaper targets correctly,
599                         // and make sure that we are positioning the wallpaper below the lower.
600                         if (wallpaperTargetIndex > oldI) {
601                             // The new target is on top of the old one.
602                             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
603                                     "Found target above old target.");
604                             mUpperWallpaperTarget = wallpaperTarget;
605                             mLowerWallpaperTarget = oldW;
606 
607                             wallpaperTarget = oldW;
608                             wallpaperTargetIndex = oldI;
609                         } else {
610                             // The new target is below the old one.
611                             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
612                                     "Found target below old target.");
613                             mUpperWallpaperTarget = oldW;
614                             mLowerWallpaperTarget = wallpaperTarget;
615                         }
616                         if (newTargetHidden && !oldTargetHidden) {
617                             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
618                                     "Old wallpaper still the target.");
619                             // Use the old target if new target is hidden but old target
620                             // is not. If they're both hidden, still use the new target.
621                             mWallpaperTarget = oldW;
622                         } else if (newTargetHidden == oldTargetHidden
623                                 && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken)
624                                     && (mService.mOpeningApps.contains(oldW.mAppToken)
625                                     || mService.mClosingApps.contains(oldW.mAppToken))) {
626                             // If they're both hidden (or both not hidden), prefer the one that's
627                             // currently in opening or closing app list, this allows transition
628                             // selection logic to better determine the wallpaper status of
629                             // opening/closing apps.
630                             mWallpaperTarget = oldW;
631                         }
632                     }
633                 }
634             }
635 
636         } else if (mLowerWallpaperTarget != null) {
637             // Is it time to stop animating?
638             if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) {
639                 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
640                 mLowerWallpaperTarget = null;
641                 mUpperWallpaperTarget = null;
642                 mWallpaperTarget = wallpaperTarget;
643                 targetChanged = true;
644             }
645         }
646 
647         result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
648         return targetChanged;
649     }
650 
updateWallpaperWindowsTargetByLayer( WindowList windows, FindWallpaperTargetResult result)651     boolean updateWallpaperWindowsTargetByLayer(
652             WindowList windows, FindWallpaperTargetResult result) {
653 
654         WindowState wallpaperTarget = result.wallpaperTarget;
655         int wallpaperTargetIndex = result.wallpaperTargetIndex;
656         boolean visible = wallpaperTarget != null;
657 
658         if (visible) {
659             // The window is visible to the compositor...but is it visible to the user?
660             // That is what the wallpaper cares about.
661             visible = isWallpaperVisible(wallpaperTarget);
662             if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
663 
664             // If the wallpaper target is animating, we may need to copy its layer adjustment.
665             // Only do this if we are not transferring between two wallpaper targets.
666             mWallpaperAnimLayerAdjustment =
667                     (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null)
668                             ? wallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0;
669 
670             final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER)
671                     + TYPE_LAYER_OFFSET;
672 
673             // Now w is the window we are supposed to be behind...  but we
674             // need to be sure to also be behind any of its attached windows,
675             // AND any starting window associated with it, AND below the
676             // maximum layer the policy allows for wallpapers.
677             while (wallpaperTargetIndex > 0) {
678                 WindowState wb = windows.get(wallpaperTargetIndex - 1);
679                 if (wb.mBaseLayer < maxLayer &&
680                         wb.mAttachedWindow != wallpaperTarget &&
681                         (wallpaperTarget.mAttachedWindow == null ||
682                                 wb.mAttachedWindow != wallpaperTarget.mAttachedWindow) &&
683                         (wb.mAttrs.type != TYPE_APPLICATION_STARTING
684                                 || wallpaperTarget.mToken == null
685                                 || wb.mToken != wallpaperTarget.mToken)) {
686                     // This window is not related to the previous one in any
687                     // interesting way, so stop here.
688                     break;
689                 }
690                 wallpaperTarget = wb;
691                 wallpaperTargetIndex--;
692             }
693         } else {
694             if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target");
695         }
696 
697         result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
698         return visible;
699     }
700 
updateWallpaperWindowsPlacement(WindowList windows, WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible)701     boolean updateWallpaperWindowsPlacement(WindowList windows,
702             WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) {
703 
704         // TODO(multidisplay): Wallpapers on main screen only.
705         final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo();
706         final int dw = displayInfo.logicalWidth;
707         final int dh = displayInfo.logicalHeight;
708 
709         // Start stepping backwards from here, ensuring that our wallpaper windows
710         // are correctly placed.
711         boolean changed = false;
712         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
713             WindowToken token = mWallpaperTokens.get(curTokenNdx);
714             if (token.hidden == visible) {
715                 if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
716                         "Wallpaper token " + token + " hidden=" + !visible);
717                 token.hidden = !visible;
718                 // Need to do a layout to ensure the wallpaper now has the correct size.
719                 mService.getDefaultDisplayContentLocked().layoutNeeded = true;
720             }
721 
722             final WindowList tokenWindows = token.windows;
723             for (int wallpaperNdx = tokenWindows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
724                 WindowState wallpaper = tokenWindows.get(wallpaperNdx);
725 
726                 if (visible) {
727                     updateWallpaperOffset(wallpaper, dw, dh, false);
728                 }
729 
730                 // First, make sure the client has the current visibility state.
731                 dispatchWallpaperVisibility(wallpaper, visible);
732 
733                 wallpaper.mWinAnimator.mAnimLayer =
734                         wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
735                 if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
736                         + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
737 
738                 // First, if this window is at the current index, then all is well.
739                 if (wallpaper == wallpaperTarget) {
740                     wallpaperTargetIndex--;
741                     wallpaperTarget = wallpaperTargetIndex > 0
742                             ? windows.get(wallpaperTargetIndex - 1) : null;
743                     continue;
744                 }
745 
746                 // The window didn't match...  the current wallpaper window,
747                 // wherever it is, is in the wrong place, so make sure it is not in the list.
748                 int oldIndex = windows.indexOf(wallpaper);
749                 if (oldIndex >= 0) {
750                     if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
751                             "Wallpaper removing at " + oldIndex + ": " + wallpaper);
752                     windows.remove(oldIndex);
753                     mService.mWindowsChanged = true;
754                     if (oldIndex < wallpaperTargetIndex) {
755                         wallpaperTargetIndex--;
756                     }
757                 }
758 
759                 // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
760                 // layer. For keyguard over wallpaper put the wallpaper under the keyguard.
761                 int insertionIndex = 0;
762                 if (visible && wallpaperTarget != null) {
763                     final int type = wallpaperTarget.mAttrs.type;
764                     final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
765                     if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0
766                             || type == TYPE_KEYGUARD_SCRIM) {
767                         insertionIndex = windows.indexOf(wallpaperTarget);
768                     }
769                 }
770                 if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
771                         || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
772                         "Moving wallpaper " + wallpaper
773                         + " from " + oldIndex + " to " + insertionIndex);
774 
775                 windows.add(insertionIndex, wallpaper);
776                 mService.mWindowsChanged = true;
777                 changed = true;
778             }
779         }
780 
781         return changed;
782     }
783 
adjustWallpaperWindows()784     boolean adjustWallpaperWindows() {
785         mService.mWindowPlacerLocked.mWallpaperMayChange = false;
786 
787         final WindowList windows = mService.getDefaultWindowListLocked();
788         // First find top-most window that has asked to be on top of the wallpaper;
789         // all wallpapers go behind it.
790         findWallpaperTarget(windows, mFindResults);
791         final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults);
792         final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults);
793         WindowState wallpaperTarget = mFindResults.wallpaperTarget;
794         int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex;
795 
796         if (wallpaperTarget == null && mFindResults.topWallpaper != null) {
797             // There is no wallpaper target, so it goes at the bottom.
798             // We will assume it is the same place as last time, if known.
799             wallpaperTarget = mFindResults.topWallpaper;
800             wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1;
801         } else {
802             // Okay i is the position immediately above the wallpaper.
803             // Look at what is below it for later.
804             wallpaperTarget = wallpaperTargetIndex > 0
805                     ? windows.get(wallpaperTargetIndex - 1) : null;
806         }
807 
808         if (visible) {
809             if (mWallpaperTarget.mWallpaperX >= 0) {
810                 mLastWallpaperX = mWallpaperTarget.mWallpaperX;
811                 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
812             }
813             if (mWallpaperTarget.mWallpaperY >= 0) {
814                 mLastWallpaperY = mWallpaperTarget.mWallpaperY;
815                 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
816             }
817             if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
818                 mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
819             }
820             if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
821                 mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
822             }
823         }
824 
825         final boolean changed = updateWallpaperWindowsPlacement(
826                 windows, wallpaperTarget, wallpaperTargetIndex, visible);
827 
828         if (targetChanged && DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target="
829                 + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper="
830                 + mUpperWallpaperTarget);
831 
832         return changed;
833     }
834 
processWallpaperDrawPendingTimeout()835     boolean processWallpaperDrawPendingTimeout() {
836         if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
837             mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
838             if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
839                     "*** WALLPAPER DRAW TIMEOUT");
840             return true;
841         }
842         return false;
843     }
844 
wallpaperTransitionReady()845     boolean wallpaperTransitionReady() {
846         boolean transitionReady = true;
847         boolean wallpaperReady = true;
848         for (int curTokenIndex = mWallpaperTokens.size() - 1;
849                 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
850             WindowToken token = mWallpaperTokens.get(curTokenIndex);
851             for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
852                     curWallpaperIndex--) {
853                 WindowState wallpaper = token.windows.get(curWallpaperIndex);
854                 if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
855                     // We've told this wallpaper to be visible, but it is not drawn yet
856                     wallpaperReady = false;
857                     if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
858                         // wait for this wallpaper until it is drawn or timeout
859                         transitionReady = false;
860                     }
861                     if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
862                         mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
863                         mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
864                         mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
865                                 WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
866                     }
867                     if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
868                             "Wallpaper should be visible but has not been drawn yet. " +
869                                     "mWallpaperDrawState=" + mWallpaperDrawState);
870                     break;
871                 }
872             }
873         }
874         if (wallpaperReady) {
875             mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
876             mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
877         }
878 
879         return transitionReady;
880     }
881 
addWallpaperToken(WindowToken token)882     void addWallpaperToken(WindowToken token) {
883         mWallpaperTokens.add(token);
884     }
885 
removeWallpaperToken(WindowToken token)886     void removeWallpaperToken(WindowToken token) {
887         mWallpaperTokens.remove(token);
888     }
889 
dump(PrintWriter pw, String prefix)890     void dump(PrintWriter pw, String prefix) {
891         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
892         if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) {
893             pw.print(prefix); pw.print("mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
894             pw.print(prefix); pw.print("mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
895         }
896         pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
897         pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
898         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
899                 || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
900             pw.print(prefix);
901             pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
902             pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
903         }
904     }
905 
dumpTokens(PrintWriter pw, String prefix, boolean dumpAll)906     void dumpTokens(PrintWriter pw, String prefix, boolean dumpAll) {
907         if (!mWallpaperTokens.isEmpty()) {
908             pw.println();
909             pw.print(prefix); pw.println("Wallpaper tokens:");
910             for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
911                 WindowToken token = mWallpaperTokens.get(i);
912                 pw.print(prefix); pw.print("Wallpaper #"); pw.print(i);
913                 pw.print(' '); pw.print(token);
914                 if (dumpAll) {
915                     pw.println(':');
916                     token.dump(pw, "    ");
917                 } else {
918                     pw.println();
919                 }
920             }
921         }
922     }
923 
924     /** Helper class for storing the results of a wallpaper target find operation. */
925     final private static class FindWallpaperTargetResult {
926         int topWallpaperIndex = 0;
927         WindowState topWallpaper = null;
928         int wallpaperTargetIndex = 0;
929         WindowState wallpaperTarget = null;
930 
setTopWallpaper(WindowState win, int index)931         void setTopWallpaper(WindowState win, int index) {
932             topWallpaper = win;
933             topWallpaperIndex = index;
934         }
935 
setWallpaperTarget(WindowState win, int index)936         void setWallpaperTarget(WindowState win, int index) {
937             wallpaperTarget = win;
938             wallpaperTargetIndex = index;
939         }
940 
reset()941         void reset() {
942             topWallpaperIndex = 0;
943             topWallpaper = null;
944             wallpaperTargetIndex = 0;
945             wallpaperTarget = null;
946         }
947     }
948 }
949