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