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 com.android.internal.util.ToBooleanFunction;
20 
21 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
22 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
24 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
25 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
26 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
27 
28 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
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 
36 import android.os.Bundle;
37 import android.os.Debug;
38 import android.os.IBinder;
39 import android.os.RemoteException;
40 import android.os.SystemClock;
41 import android.util.ArraySet;
42 import android.util.Slog;
43 import android.view.DisplayInfo;
44 import android.view.WindowManager;
45 import android.view.animation.Animation;
46 
47 import java.io.PrintWriter;
48 import java.util.ArrayList;
49 
50 /**
51  * Controls wallpaper windows visibility, ordering, and so on.
52  * NOTE: All methods in this class must be called with the window manager service lock held.
53  */
54 class WallpaperController {
55     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
56     private WindowManagerService mService;
57 
58     private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
59 
60     // If non-null, this is the currently visible window that is associated
61     // with the wallpaper.
62     private WindowState mWallpaperTarget = null;
63     // If non-null, we are in the middle of animating from one wallpaper target
64     // to another, and this is the previous wallpaper target.
65     private WindowState mPrevWallpaperTarget = null;
66 
67     private int mWallpaperAnimLayerAdjustment;
68 
69     private float mLastWallpaperX = -1;
70     private float mLastWallpaperY = -1;
71     private float mLastWallpaperXStep = -1;
72     private float mLastWallpaperYStep = -1;
73     private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
74     private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
75 
76     // This is set when we are waiting for a wallpaper to tell us it is done
77     // changing its scroll position.
78     private WindowState mWaitingOnWallpaper;
79 
80     // The last time we had a timeout when waiting for a wallpaper.
81     private long mLastWallpaperTimeoutTime;
82     // We give a wallpaper up to 150ms to finish scrolling.
83     private static final long WALLPAPER_TIMEOUT = 150;
84     // Time we wait after a timeout before trying to wait again.
85     private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
86 
87     // Set to the wallpaper window we would like to hide once the transition animations are done.
88     // This is useful in cases where we don't want the wallpaper to be hidden when the close app
89     // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
90     // target and isn't done animating in.
91     WindowState mDeferredHideWallpaper = null;
92 
93     // We give a wallpaper up to 500ms to finish drawing before playing app transitions.
94     private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500;
95     private static final int WALLPAPER_DRAW_NORMAL = 0;
96     private static final int WALLPAPER_DRAW_PENDING = 1;
97     private static final int WALLPAPER_DRAW_TIMEOUT = 2;
98     private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
99 
100     private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
101 
102     private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
103         final WindowAnimator winAnimator = mService.mAnimator;
104         if ((w.mAttrs.type == TYPE_WALLPAPER)) {
105             if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
106                 mFindResults.setTopWallpaper(w);
107                 mFindResults.resetTopWallpaper = false;
108             }
109             return false;
110         }
111 
112         mFindResults.resetTopWallpaper = true;
113         if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
114             // If this window's app token is hidden and not animating, it is of no interest to us.
115             if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
116                 if (DEBUG_WALLPAPER) Slog.v(TAG,
117                         "Skipping hidden and not animating token: " + w);
118                 return false;
119             }
120         }
121         if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
122                 + " mDrawState=" + w.mWinAnimator.mDrawState);
123 
124         if (w.mWillReplaceWindow && mWallpaperTarget == null
125                 && !mFindResults.useTopWallpaperAsTarget) {
126             // When we are replacing a window and there was wallpaper before replacement, we want to
127             // keep the window until the new windows fully appear and can determine the visibility,
128             // to avoid flickering.
129             mFindResults.setUseTopWallpaperAsTarget(true);
130         }
131 
132         final boolean keyguardGoingAwayWithWallpaper = (w.mAppToken != null
133                 && AppTransition.isKeyguardGoingAwayTransit(
134                 w.mAppToken.mAppAnimator.getTransit())
135                 && (w.mAppToken.mAppAnimator.getTransitFlags()
136                 & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
137 
138         boolean needsShowWhenLockedWallpaper = false;
139         if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
140                 && mService.mPolicy.isKeyguardLocked()
141                 && mService.mPolicy.isKeyguardOccluded()) {
142             // The lowest show when locked window decides whether we need to put the wallpaper
143             // behind.
144             needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs)
145                     || (w.mAppToken != null && !w.mAppToken.fillsParent());
146         }
147 
148         if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) {
149             // Keep the wallpaper during Keyguard exit but also when it's needed for a
150             // non-fullscreen show when locked activity.
151             mFindResults.setUseTopWallpaperAsTarget(true);
152         }
153 
154         final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
155         if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
156             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
157             mFindResults.setWallpaperTarget(w);
158             if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
159                 // The current wallpaper target is animating, so we'll look behind it for
160                 // another possible target and figure out what is going on later.
161                 if (DEBUG_WALLPAPER) Slog.v(TAG,
162                         "Win " + w + ": token animating, looking behind.");
163             }
164             // Found a target! End search.
165             return true;
166         } else if (w == winAnimator.mWindowDetachedWallpaper) {
167             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
168                     "Found animating detached wallpaper target win: " + w);
169             mFindResults.setUseTopWallpaperAsTarget(true);
170         }
171         return false;
172     };
173 
WallpaperController(WindowManagerService service)174     public WallpaperController(WindowManagerService service) {
175         mService = service;
176     }
177 
getWallpaperTarget()178     WindowState getWallpaperTarget() {
179         return mWallpaperTarget;
180     }
181 
isWallpaperTarget(WindowState win)182     boolean isWallpaperTarget(WindowState win) {
183         return win == mWallpaperTarget;
184     }
185 
isBelowWallpaperTarget(WindowState win)186     boolean isBelowWallpaperTarget(WindowState win) {
187         return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer;
188     }
189 
isWallpaperVisible()190     boolean isWallpaperVisible() {
191         return isWallpaperVisible(mWallpaperTarget);
192     }
193 
194     /**
195      * Starts {@param a} on all wallpaper windows.
196      */
startWallpaperAnimation(Animation a)197     void startWallpaperAnimation(Animation a) {
198         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
199             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
200             token.startAnimation(a);
201         }
202     }
203 
isWallpaperVisible(WindowState wallpaperTarget)204     private boolean isWallpaperVisible(WindowState wallpaperTarget) {
205         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
206                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
207                 + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
208                 ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
209                 + " prev=" + mPrevWallpaperTarget);
210         return (wallpaperTarget != null
211                 && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
212                 && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
213                 || mPrevWallpaperTarget != null;
214     }
215 
isWallpaperTargetAnimating()216     boolean isWallpaperTargetAnimating() {
217         return mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimationSet()
218                 && !mWallpaperTarget.mWinAnimator.isDummyAnimation();
219     }
220 
updateWallpaperVisibility()221     void updateWallpaperVisibility() {
222         final boolean visible = isWallpaperVisible(mWallpaperTarget);
223 
224         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
225             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
226             token.updateWallpaperVisibility(visible);
227         }
228     }
229 
hideDeferredWallpapersIfNeeded()230     void hideDeferredWallpapersIfNeeded() {
231         if (mDeferredHideWallpaper != null) {
232             hideWallpapers(mDeferredHideWallpaper);
233             mDeferredHideWallpaper = null;
234         }
235     }
236 
hideWallpapers(final WindowState winGoingAway)237     void hideWallpapers(final WindowState winGoingAway) {
238         if (mWallpaperTarget != null
239                 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
240             return;
241         }
242         if (mService.mAppTransition.isRunning()) {
243             // Defer hiding the wallpaper when app transition is running until the animations
244             // are done.
245             mDeferredHideWallpaper = winGoingAway;
246             return;
247         }
248 
249         final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
250         for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
251             final WallpaperWindowToken token = mWallpaperTokens.get(i);
252             token.hideWallpaperToken(wasDeferred, "hideWallpapers");
253             if (DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG, "Hiding wallpaper " + token
254                     + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
255                     + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
256         }
257     }
258 
updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync)259     boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
260         boolean rawChanged = false;
261         // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
262         // match the behavior of most Launchers
263         float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
264         float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
265         float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
266         int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
267         int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
268         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
269             offset += mLastWallpaperDisplayOffsetX;
270         }
271         boolean changed = wallpaperWin.mXOffset != offset;
272         if (changed) {
273             if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
274             wallpaperWin.mXOffset = offset;
275         }
276         if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
277             wallpaperWin.mWallpaperX = wpx;
278             wallpaperWin.mWallpaperXStep = wpxs;
279             rawChanged = true;
280         }
281 
282         float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
283         float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
284         int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
285         offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
286         if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
287             offset += mLastWallpaperDisplayOffsetY;
288         }
289         if (wallpaperWin.mYOffset != offset) {
290             if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
291             changed = true;
292             wallpaperWin.mYOffset = offset;
293         }
294         if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
295             wallpaperWin.mWallpaperY = wpy;
296             wallpaperWin.mWallpaperYStep = wpys;
297             rawChanged = true;
298         }
299 
300         if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
301                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
302             try {
303                 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
304                         + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
305                         + " y=" + wallpaperWin.mWallpaperY);
306                 if (sync) {
307                     mWaitingOnWallpaper = wallpaperWin;
308                 }
309                 wallpaperWin.mClient.dispatchWallpaperOffsets(
310                         wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
311                         wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
312                 if (sync) {
313                     if (mWaitingOnWallpaper != null) {
314                         long start = SystemClock.uptimeMillis();
315                         if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY)
316                                 < start) {
317                             try {
318                                 if (DEBUG_WALLPAPER) Slog.v(TAG,
319                                         "Waiting for offset complete...");
320                                 mService.mWindowMap.wait(WALLPAPER_TIMEOUT);
321                             } catch (InterruptedException e) {
322                             }
323                             if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!");
324                             if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) {
325                                 Slog.i(TAG, "Timeout waiting for wallpaper to offset: "
326                                         + wallpaperWin);
327                                 mLastWallpaperTimeoutTime = start;
328                             }
329                         }
330                         mWaitingOnWallpaper = null;
331                     }
332                 }
333             } catch (RemoteException e) {
334             }
335         }
336 
337         return changed;
338     }
339 
setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)340     void setWindowWallpaperPosition(
341             WindowState window, float x, float y, float xStep, float yStep) {
342         if (window.mWallpaperX != x || window.mWallpaperY != y)  {
343             window.mWallpaperX = x;
344             window.mWallpaperY = y;
345             window.mWallpaperXStep = xStep;
346             window.mWallpaperYStep = yStep;
347             updateWallpaperOffsetLocked(window, true);
348         }
349     }
350 
setWindowWallpaperDisplayOffset(WindowState window, int x, int y)351     void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
352         if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y)  {
353             window.mWallpaperDisplayOffsetX = x;
354             window.mWallpaperDisplayOffsetY = y;
355             updateWallpaperOffsetLocked(window, true);
356         }
357     }
358 
sendWindowWallpaperCommand( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)359     Bundle sendWindowWallpaperCommand(
360             WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
361         if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
362             boolean doWait = sync;
363             for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
364                 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
365                 token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
366             }
367 
368             if (doWait) {
369                 // TODO: Need to wait for result.
370             }
371         }
372 
373         return null;
374     }
375 
updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)376     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
377         final DisplayContent displayContent = changingTarget.getDisplayContent();
378         if (displayContent == null) {
379             return;
380         }
381         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
382         final int dw = displayInfo.logicalWidth;
383         final int dh = displayInfo.logicalHeight;
384 
385         WindowState target = mWallpaperTarget;
386         if (target != null) {
387             if (target.mWallpaperX >= 0) {
388                 mLastWallpaperX = target.mWallpaperX;
389             } else if (changingTarget.mWallpaperX >= 0) {
390                 mLastWallpaperX = changingTarget.mWallpaperX;
391             }
392             if (target.mWallpaperY >= 0) {
393                 mLastWallpaperY = target.mWallpaperY;
394             } else if (changingTarget.mWallpaperY >= 0) {
395                 mLastWallpaperY = changingTarget.mWallpaperY;
396             }
397             if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
398                 mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
399             } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
400                 mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
401             }
402             if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
403                 mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
404             } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
405                 mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
406             }
407             if (target.mWallpaperXStep >= 0) {
408                 mLastWallpaperXStep = target.mWallpaperXStep;
409             } else if (changingTarget.mWallpaperXStep >= 0) {
410                 mLastWallpaperXStep = changingTarget.mWallpaperXStep;
411             }
412             if (target.mWallpaperYStep >= 0) {
413                 mLastWallpaperYStep = target.mWallpaperYStep;
414             } else if (changingTarget.mWallpaperYStep >= 0) {
415                 mLastWallpaperYStep = changingTarget.mWallpaperYStep;
416             }
417         }
418 
419         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
420             mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(dw, dh, sync);
421         }
422     }
423 
clearLastWallpaperTimeoutTime()424     void clearLastWallpaperTimeoutTime() {
425         mLastWallpaperTimeoutTime = 0;
426     }
427 
wallpaperCommandComplete(IBinder window)428     void wallpaperCommandComplete(IBinder window) {
429         if (mWaitingOnWallpaper != null &&
430                 mWaitingOnWallpaper.mClient.asBinder() == window) {
431             mWaitingOnWallpaper = null;
432             mService.mWindowMap.notifyAll();
433         }
434     }
435 
wallpaperOffsetsComplete(IBinder window)436     void wallpaperOffsetsComplete(IBinder window) {
437         if (mWaitingOnWallpaper != null &&
438                 mWaitingOnWallpaper.mClient.asBinder() == window) {
439             mWaitingOnWallpaper = null;
440             mService.mWindowMap.notifyAll();
441         }
442     }
443 
getAnimLayerAdjustment()444     int getAnimLayerAdjustment() {
445         return mWallpaperAnimLayerAdjustment;
446     }
447 
findWallpaperTarget(DisplayContent dc)448     private void findWallpaperTarget(DisplayContent dc) {
449         mFindResults.reset();
450         if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
451             // In freeform mode we set the wallpaper as its own target, so we don't need an
452             // additional window to make it visible.
453             mFindResults.setUseTopWallpaperAsTarget(true);
454         }
455 
456         dc.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
457 
458         if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
459             mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
460         }
461     }
462 
isFullscreen(WindowManager.LayoutParams attrs)463     private boolean isFullscreen(WindowManager.LayoutParams attrs) {
464         return attrs.x == 0 && attrs.y == 0
465                 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT;
466     }
467 
468     /** Updates the target wallpaper if needed and returns true if an update happened. */
updateWallpaperWindowsTarget(DisplayContent dc, FindWallpaperTargetResult result)469     private void updateWallpaperWindowsTarget(DisplayContent dc,
470             FindWallpaperTargetResult result) {
471 
472         WindowState wallpaperTarget = result.wallpaperTarget;
473 
474         if (mWallpaperTarget == wallpaperTarget
475                 || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) {
476 
477             if (mPrevWallpaperTarget == null) {
478                 return;
479             }
480 
481             // Is it time to stop animating?
482             if (!mPrevWallpaperTarget.isAnimatingLw()) {
483                 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
484                 mPrevWallpaperTarget = null;
485                 mWallpaperTarget = wallpaperTarget;
486             }
487             return;
488         }
489 
490         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
491                 "New wallpaper target: " + wallpaperTarget + " prevTarget: " + mWallpaperTarget);
492 
493         mPrevWallpaperTarget = null;
494 
495         final WindowState prevWallpaperTarget = mWallpaperTarget;
496         mWallpaperTarget = wallpaperTarget;
497 
498         if (wallpaperTarget == null || prevWallpaperTarget == null) {
499             return;
500         }
501 
502         // Now what is happening...  if the current and new targets are animating,
503         // then we are in our super special mode!
504         boolean oldAnim = prevWallpaperTarget.isAnimatingLw();
505         boolean foundAnim = wallpaperTarget.isAnimatingLw();
506         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
507                 "New animation: " + foundAnim + " old animation: " + oldAnim);
508 
509         if (!foundAnim || !oldAnim) {
510             return;
511         }
512 
513         if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
514             return;
515         }
516 
517         final boolean newTargetHidden = wallpaperTarget.mAppToken != null
518                 && wallpaperTarget.mAppToken.hiddenRequested;
519         final boolean oldTargetHidden = prevWallpaperTarget.mAppToken != null
520                 && prevWallpaperTarget.mAppToken.hiddenRequested;
521 
522         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
523                 + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
524                 + " hidden=" + newTargetHidden);
525 
526         mPrevWallpaperTarget = prevWallpaperTarget;
527 
528         if (newTargetHidden && !oldTargetHidden) {
529             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target.");
530             // Use the old target if new target is hidden but old target
531             // is not. If they're both hidden, still use the new target.
532             mWallpaperTarget = prevWallpaperTarget;
533         } else if (newTargetHidden == oldTargetHidden
534                 && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken)
535                 && (mService.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
536                 || mService.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
537             // If they're both hidden (or both not hidden), prefer the one that's currently in
538             // opening or closing app list, this allows transition selection logic to better
539             // determine the wallpaper status of opening/closing apps.
540             mWallpaperTarget = prevWallpaperTarget;
541         }
542 
543         result.setWallpaperTarget(wallpaperTarget);
544     }
545 
updateWallpaperTokens(boolean visible)546     private void updateWallpaperTokens(boolean visible) {
547         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
548             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
549             token.updateWallpaperWindows(visible, mWallpaperAnimLayerAdjustment);
550             token.getDisplayContent().assignWindowLayers(false);
551         }
552     }
553 
adjustWallpaperWindows(DisplayContent dc)554     void adjustWallpaperWindows(DisplayContent dc) {
555         mService.mRoot.mWallpaperMayChange = false;
556 
557         // First find top-most window that has asked to be on top of the wallpaper;
558         // all wallpapers go behind it.
559         findWallpaperTarget(dc);
560         updateWallpaperWindowsTarget(dc, mFindResults);
561 
562         // The window is visible to the compositor...but is it visible to the user?
563         // That is what the wallpaper cares about.
564         final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
565         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
566 
567         if (visible) {
568             // If the wallpaper target is animating, we may need to copy its layer adjustment.
569             // Only do this if we are not transferring between two wallpaper targets.
570             mWallpaperAnimLayerAdjustment =
571                     (mPrevWallpaperTarget == null && mWallpaperTarget.mAppToken != null)
572                             ? mWallpaperTarget.mAppToken.getAnimLayerAdjustment() : 0;
573 
574             if (mWallpaperTarget.mWallpaperX >= 0) {
575                 mLastWallpaperX = mWallpaperTarget.mWallpaperX;
576                 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
577             }
578             if (mWallpaperTarget.mWallpaperY >= 0) {
579                 mLastWallpaperY = mWallpaperTarget.mWallpaperY;
580                 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
581             }
582             if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
583                 mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
584             }
585             if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
586                 mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
587             }
588         }
589 
590         updateWallpaperTokens(visible);
591 
592         if (DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
593                 + " prev=" + mPrevWallpaperTarget);
594     }
595 
processWallpaperDrawPendingTimeout()596     boolean processWallpaperDrawPendingTimeout() {
597         if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
598             mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
599             if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
600                     "*** WALLPAPER DRAW TIMEOUT");
601             return true;
602         }
603         return false;
604     }
605 
wallpaperTransitionReady()606     boolean wallpaperTransitionReady() {
607         boolean transitionReady = true;
608         boolean wallpaperReady = true;
609         for (int curTokenIndex = mWallpaperTokens.size() - 1;
610                 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
611             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex);
612             if (token.hasVisibleNotDrawnWallpaper()) {
613                 // We've told this wallpaper to be visible, but it is not drawn yet
614                 wallpaperReady = false;
615                 if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
616                     // wait for this wallpaper until it is drawn or timeout
617                     transitionReady = false;
618                 }
619                 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
620                     mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
621                     mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
622                     mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
623                             WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
624                 }
625                 if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
626                         "Wallpaper should be visible but has not been drawn yet. " +
627                                 "mWallpaperDrawState=" + mWallpaperDrawState);
628                 break;
629             }
630         }
631         if (wallpaperReady) {
632             mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
633             mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
634         }
635 
636         return transitionReady;
637     }
638 
639     /**
640      * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
641      * the opening apps should be a wallpaper target.
642      */
adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc, ArraySet<AppWindowToken> openingApps)643     void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
644             ArraySet<AppWindowToken> openingApps) {
645         boolean adjust = false;
646         if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
647             adjust = true;
648         } else {
649             for (int i = openingApps.size() - 1; i >= 0; --i) {
650                 final AppWindowToken token = openingApps.valueAt(i);
651                 if (token.windowsCanBeWallpaperTarget()) {
652                     adjust = true;
653                     break;
654                 }
655             }
656         }
657 
658         if (adjust) {
659             adjustWallpaperWindows(dc);
660         }
661     }
662 
addWallpaperToken(WallpaperWindowToken token)663     void addWallpaperToken(WallpaperWindowToken token) {
664         mWallpaperTokens.add(token);
665     }
666 
removeWallpaperToken(WallpaperWindowToken token)667     void removeWallpaperToken(WallpaperWindowToken token) {
668         mWallpaperTokens.remove(token);
669     }
670 
dump(PrintWriter pw, String prefix)671     void dump(PrintWriter pw, String prefix) {
672         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
673         if (mPrevWallpaperTarget != null) {
674             pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
675         }
676         pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
677         pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
678         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
679                 || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
680             pw.print(prefix);
681             pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
682             pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
683         }
684 
685         if (mWallpaperAnimLayerAdjustment != 0) {
686             pw.println(prefix + "mWallpaperAnimLayerAdjustment=" + mWallpaperAnimLayerAdjustment);
687         }
688     }
689 
690     /** Helper class for storing the results of a wallpaper target find operation. */
691     final private static class FindWallpaperTargetResult {
692         WindowState topWallpaper = null;
693         boolean useTopWallpaperAsTarget = false;
694         WindowState wallpaperTarget = null;
695         boolean resetTopWallpaper = false;
696 
setTopWallpaper(WindowState win)697         void setTopWallpaper(WindowState win) {
698             topWallpaper = win;
699         }
700 
setWallpaperTarget(WindowState win)701         void setWallpaperTarget(WindowState win) {
702             wallpaperTarget = win;
703         }
704 
setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget)705         void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) {
706             useTopWallpaperAsTarget = topWallpaperAsTarget;
707         }
708 
reset()709         void reset() {
710             topWallpaper = null;
711             wallpaperTarget = null;
712             useTopWallpaperAsTarget = false;
713             resetTopWallpaper = false;
714         }
715     }
716 }
717