Moved window manager wallpaper control into separate class
Change-Id: Ia3c12065678992614667dc210d4611a1250ca22b
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
new file mode 100644
index 0000000..b871d7f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerService.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER_LIGHT;
+import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls wallpaper windows visibility, ordering, and so on.
+ * NOTE: All methods in this class must be called with the window manager service lock held.
+ */
+class WallpaperController {
+ private static final String TAG = com.android.server.wm.WindowManagerService.TAG;
+ final private WindowManagerService mService;
+
+ private final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<>();
+
+ // If non-null, this is the currently visible window that is associated
+ // with the wallpaper.
+ private WindowState mWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the lower one in Z-order.
+ private WindowState mLowerWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the higher one in Z-order.
+ private WindowState mUpperWallpaperTarget = null;
+
+ private int mWallpaperAnimLayerAdjustment;
+
+ private float mLastWallpaperX = -1;
+ private float mLastWallpaperY = -1;
+ private float mLastWallpaperXStep = -1;
+ private float mLastWallpaperYStep = -1;
+ private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
+ private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
+
+ // This is set when we are waiting for a wallpaper to tell us it is done
+ // changing its scroll position.
+ WindowState mWaitingOnWallpaper;
+
+ // The last time we had a timeout when waiting for a wallpaper.
+ private long mLastWallpaperTimeoutTime;
+ // We give a wallpaper up to 150ms to finish scrolling.
+ private static final long WALLPAPER_TIMEOUT = 150;
+ // Time we wait after a timeout before trying to wait again.
+ private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
+
+ // Set to the wallpaper window we would like to hide once the transition animations are done.
+ // This is useful in cases where we don't want the wallpaper to be hidden when the close app
+ // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
+ // target and isn't done animating in.
+ private WindowState mDeferredHideWallpaper = null;
+
+ // We give a wallpaper up to 500ms to finish drawing before playing app transitions.
+ private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500;
+ private static final int WALLPAPER_DRAW_NORMAL = 0;
+ private static final int WALLPAPER_DRAW_PENDING = 1;
+ private static final int WALLPAPER_DRAW_TIMEOUT = 2;
+ private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+
+ public WallpaperController(WindowManagerService service) {
+ mService = service;
+ }
+
+ WindowState getWallpaperTarget() {
+ return mWallpaperTarget;
+ }
+
+ WindowState getLowerWallpaperTarget() {
+ return mLowerWallpaperTarget;
+ }
+
+ WindowState getUpperWallpaperTarget() {
+ return mUpperWallpaperTarget;
+ }
+
+ boolean isWallpaperTarget(WindowState win) {
+ return win == mWallpaperTarget;
+ }
+
+ boolean isBelowWallpaperTarget(WindowState win) {
+ return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer;
+ }
+
+ boolean isWallpaperVisible() {
+ return isWallpaperVisible(mWallpaperTarget);
+ }
+
+ private boolean isWallpaperVisible(WindowState wallpaperTarget) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
+ ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
+ + " upper=" + mUpperWallpaperTarget
+ + " lower=" + mLowerWallpaperTarget);
+ return (wallpaperTarget != null
+ && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
+ && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
+ || mUpperWallpaperTarget != null
+ || mLowerWallpaperTarget != null;
+ }
+
+ boolean isWallpaperTargetAnimating() {
+ return mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimating()
+ && !mWallpaperTarget.mWinAnimator.isDummyAnimation();
+ }
+
+ void updateWallpaperVisibility() {
+ final boolean visible = isWallpaperVisible(mWallpaperTarget);
+ final DisplayContent displayContent = mWallpaperTarget.getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+
+ for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+ WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ if (token.hidden == visible) {
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the
+ // correct size.
+ displayContent.layoutNeeded = true;
+ }
+
+ final WindowList windows = token.windows;
+ for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ WindowState wallpaper = windows.get(wallpaperNdx);
+ if (visible) {
+ updateWallpaperOffset(wallpaper, dw, dh, false);
+ }
+
+ dispatchWallpaperVisibility(wallpaper, visible);
+ }
+ }
+ }
+
+ void hideDeferredWallpapersIfNeeded() {
+ if (mDeferredHideWallpaper != null) {
+ hideWallpapers(mDeferredHideWallpaper);
+ mDeferredHideWallpaper = null;
+ }
+ }
+
+ void hideWallpapers(final WindowState winGoingAway) {
+ if (mWallpaperTarget != null
+ && (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+ return;
+ }
+ if (mService.mAppTransition.isRunning()) {
+ // Defer hiding the wallpaper when app transition is running until the animations
+ // are done.
+ mDeferredHideWallpaper = winGoingAway;
+ return;
+ }
+
+ final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mWallpaperTokens.get(i);
+ for (int j = token.windows.size() - 1; j >= 0; j--) {
+ final WindowState wallpaper = token.windows.get(j);
+ final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
+ if (!winAnimator.mLastHidden || wasDeferred) {
+ winAnimator.hide();
+ dispatchWallpaperVisibility(wallpaper, false);
+ final DisplayContent displayContent = wallpaper.getDisplayContent();
+ if (displayContent != null) {
+ displayContent.pendingLayoutChanges |=
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ }
+ }
+ if (DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG, "Hiding wallpaper " + token
+ + " from " + winGoingAway + " target=" + mWallpaperTarget + " lower="
+ + mLowerWallpaperTarget + "\n" + Debug.getCallers(5, " "));
+ token.hidden = true;
+ }
+ }
+
+ /**
+ * Check wallpaper for visibility change and notify window if so.
+ * @param wallpaper The wallpaper to test and notify.
+ * @param visible Current visibility.
+ */
+ void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
+ // Only send notification if the visibility actually changed and we are not trying to hide
+ // the wallpaper when we are deferring hiding of the wallpaper.
+ if (wallpaper.mWallpaperVisible != visible
+ && (mDeferredHideWallpaper == null || visible)) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+ "Updating vis of wallpaper " + wallpaper
+ + ": " + visible + " from:\n" + Debug.getCallers(4, " "));
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
+ boolean rawChanged = false;
+ float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
+ float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
+ int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
+ int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
+ if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ offset += mLastWallpaperDisplayOffsetX;
+ }
+ boolean changed = wallpaperWin.mXOffset != offset;
+ if (changed) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset);
+ wallpaperWin.mXOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
+ wallpaperWin.mWallpaperX = wpx;
+ wallpaperWin.mWallpaperXStep = wpxs;
+ rawChanged = true;
+ }
+
+ float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
+ float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
+ int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
+ offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
+ if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ offset += mLastWallpaperDisplayOffsetY;
+ }
+ if (wallpaperWin.mYOffset != offset) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset);
+ changed = true;
+ wallpaperWin.mYOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
+ wallpaperWin.mWallpaperY = wpy;
+ wallpaperWin.mWallpaperYStep = wpys;
+ rawChanged = true;
+ }
+
+ if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
+ try {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
+ + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
+ + " y=" + wallpaperWin.mWallpaperY);
+ if (sync) {
+ mWaitingOnWallpaper = wallpaperWin;
+ }
+ wallpaperWin.mClient.dispatchWallpaperOffsets(
+ wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
+ wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
+ if (sync) {
+ if (mWaitingOnWallpaper != null) {
+ long start = SystemClock.uptimeMillis();
+ if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY)
+ < start) {
+ try {
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ "Waiting for offset complete...");
+ mService.mWindowMap.wait(WALLPAPER_TIMEOUT);
+ } catch (InterruptedException e) {
+ }
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!");
+ if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) {
+ Slog.i(TAG, "Timeout waiting for wallpaper to offset: "
+ + wallpaperWin);
+ mLastWallpaperTimeoutTime = start;
+ }
+ }
+ mWaitingOnWallpaper = null;
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ return changed;
+ }
+
+ void setWindowWallpaperPosition(
+ WindowState window, float x, float y, float xStep, float yStep) {
+ if (window.mWallpaperX != x || window.mWallpaperY != y) {
+ window.mWallpaperX = x;
+ window.mWallpaperY = y;
+ window.mWallpaperXStep = xStep;
+ window.mWallpaperYStep = yStep;
+ updateWallpaperOffsetLocked(window, true);
+ }
+ }
+
+ void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
+ if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
+ window.mWallpaperDisplayOffsetX = x;
+ window.mWallpaperDisplayOffsetY = y;
+ updateWallpaperOffsetLocked(window, true);
+ }
+ }
+
+ Bundle sendWindowWallpaperCommand(
+ WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
+ if (window == mWallpaperTarget
+ || window == mLowerWallpaperTarget
+ || window == mUpperWallpaperTarget) {
+ boolean doWait = sync;
+ for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+ final WindowList windows = mWallpaperTokens.get(curTokenNdx).windows;
+ for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ WindowState wallpaper = windows.get(wallpaperNdx);
+ try {
+ wallpaper.mClient.dispatchWallpaperCommand(action,
+ x, y, z, extras, sync);
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ if (doWait) {
+ // TODO: Need to wait for result.
+ }
+ }
+
+ return null;
+ }
+
+ void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
+ final DisplayContent displayContent = changingTarget.getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+
+ WindowState target = mWallpaperTarget;
+ if (target != null) {
+ if (target.mWallpaperX >= 0) {
+ mLastWallpaperX = target.mWallpaperX;
+ } else if (changingTarget.mWallpaperX >= 0) {
+ mLastWallpaperX = changingTarget.mWallpaperX;
+ }
+ if (target.mWallpaperY >= 0) {
+ mLastWallpaperY = target.mWallpaperY;
+ } else if (changingTarget.mWallpaperY >= 0) {
+ mLastWallpaperY = changingTarget.mWallpaperY;
+ }
+ if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
+ } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
+ }
+ if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
+ } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
+ }
+ if (target.mWallpaperXStep >= 0) {
+ mLastWallpaperXStep = target.mWallpaperXStep;
+ } else if (changingTarget.mWallpaperXStep >= 0) {
+ mLastWallpaperXStep = changingTarget.mWallpaperXStep;
+ }
+ if (target.mWallpaperYStep >= 0) {
+ mLastWallpaperYStep = target.mWallpaperYStep;
+ } else if (changingTarget.mWallpaperYStep >= 0) {
+ mLastWallpaperYStep = changingTarget.mWallpaperYStep;
+ }
+ }
+
+ for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+ WindowList windows = mWallpaperTokens.get(curTokenNdx).windows;
+ for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ WindowState wallpaper = windows.get(wallpaperNdx);
+ if (updateWallpaperOffset(wallpaper, dw, dh, sync)) {
+ WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
+ winAnimator.computeShownFrameLocked();
+ // No need to lay out the windows - we can just set the wallpaper position
+ // directly.
+ winAnimator.setWallpaperOffset(wallpaper.mShownFrame);
+ // We only want to be synchronous with one wallpaper.
+ sync = false;
+ }
+ }
+ }
+ }
+
+ void clearLastWallpaperTimeoutTime() {
+ mLastWallpaperTimeoutTime = 0;
+ }
+
+ void wallpaperCommandComplete(IBinder window) {
+ if (mWaitingOnWallpaper != null &&
+ mWaitingOnWallpaper.mClient.asBinder() == window) {
+ mWaitingOnWallpaper = null;
+ mService.mWindowMap.notifyAll();
+ }
+ }
+
+ void wallpaperOffsetsComplete(IBinder window) {
+ if (mWaitingOnWallpaper != null &&
+ mWaitingOnWallpaper.mClient.asBinder() == window) {
+ mWaitingOnWallpaper = null;
+ mService.mWindowMap.notifyAll();
+ }
+ }
+
+ int getAnimLayerAdjustment() {
+ return mWallpaperAnimLayerAdjustment;
+ }
+
+ void setAnimLayerAdjustment(WindowState win, int adj) {
+ if (win != mWallpaperTarget || mLowerWallpaperTarget != null) {
+ return;
+ }
+
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Setting wallpaper layer adj to " + adj);
+ mWallpaperAnimLayerAdjustment = adj;
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ WindowList windows = mWallpaperTokens.get(i).windows;
+ for (int j = windows.size() - 1; j >= 0; j--) {
+ WindowState wallpaper = windows.get(j);
+ wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + adj;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "setWallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
+ }
+ }
+ }
+
+ boolean adjustWallpaperWindows() {
+ mService.mInnerFields.mWallpaperMayChange = false;
+ boolean targetChanged = false;
+
+ // TODO(multidisplay): Wallpapers on main screen only.
+ final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+
+ final WindowAnimator winAnimator = mService.mAnimator;
+
+ // First find top-most window that has asked to be on top of the
+ // wallpaper; all wallpapers go behind it.
+ final WindowList windows = mService.getDefaultWindowListLocked();
+ int N = windows.size();
+ WindowState w = null;
+ WindowState foundW = null;
+ int foundI = 0;
+ WindowState topCurW = null;
+ int topCurI = 0;
+ int windowDetachedI = -1;
+ int i = N;
+ while (i > 0) {
+ i--;
+ w = windows.get(i);
+ if ((w.mAttrs.type == TYPE_WALLPAPER)) {
+ if (topCurW == null) {
+ topCurW = w;
+ topCurI = i;
+ }
+ continue;
+ }
+ topCurW = null;
+ if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
+ // If this window's app token is hidden and not animating,
+ // it is of no interest to us.
+ if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ "Skipping hidden and not animating token: " + w);
+ continue;
+ }
+ }
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen="
+ + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState);
+
+ // If the app is executing an animation because the keyguard is going away, keep the
+ // wallpaper during the animation so it doesn't flicker out.
+ final boolean hasWallpaper = (w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+ || (w.mAppToken != null
+ && w.mWinAnimator.mKeyguardGoingAwayAnimation);
+ if (hasWallpaper && w.isOnScreen()
+ && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ "Found wallpaper target: #" + i + "=" + w);
+ foundW = w;
+ foundI = i;
+ if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) {
+ // The current wallpaper target is animating, so we'll
+ // look behind it for another possible target and figure
+ // out what is going on below.
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w
+ + ": token animating, looking behind.");
+ continue;
+ }
+ break;
+ } else if (w == winAnimator.mWindowDetachedWallpaper) {
+ windowDetachedI = i;
+ }
+ }
+
+ if (foundW == null && windowDetachedI >= 0) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+ "Found animating detached wallpaper activity: #" + i + "=" + w);
+ foundW = w;
+ foundI = windowDetachedI;
+ }
+
+ if (mWallpaperTarget != foundW
+ && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) {
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "New wallpaper target: " + foundW
+ + " oldTarget: " + mWallpaperTarget);
+ }
+
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+
+ WindowState oldW = mWallpaperTarget;
+ mWallpaperTarget = foundW;
+ targetChanged = true;
+
+ // Now what is happening... if the current and new targets are
+ // animating, then we are in our super special mode!
+ if (foundW != null && oldW != null) {
+ boolean oldAnim = oldW.isAnimatingLw();
+ boolean foundAnim = foundW.isAnimatingLw();
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "New animation: " + foundAnim
+ + " old animation: " + oldAnim);
+ }
+ if (foundAnim && oldAnim) {
+ int oldI = windows.indexOf(oldW);
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "New i: " + foundI + " old i: " + oldI);
+ }
+ if (oldI >= 0) {
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "Animating wallpapers: old#" + oldI
+ + "=" + oldW + "; new#" + foundI
+ + "=" + foundW);
+ }
+
+ // Set the new target correctly.
+ if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) {
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "Old wallpaper still the target.");
+ }
+ mWallpaperTarget = oldW;
+ foundW = oldW;
+ foundI = oldI;
+ }
+ // Now set the upper and lower wallpaper targets
+ // correctly, and make sure that we are positioning
+ // the wallpaper below the lower.
+ else if (foundI > oldI) {
+ // The new target is on top of the old one.
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "Found target above old target.");
+ }
+ mUpperWallpaperTarget = foundW;
+ mLowerWallpaperTarget = oldW;
+ foundW = oldW;
+ foundI = oldI;
+ } else {
+ // The new target is below the old one.
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "Found target below old target.");
+ }
+ mUpperWallpaperTarget = oldW;
+ mLowerWallpaperTarget = foundW;
+ }
+ }
+ }
+ }
+
+ } else if (mLowerWallpaperTarget != null) {
+ // Is it time to stop animating?
+ if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) {
+ if (DEBUG_WALLPAPER_LIGHT) {
+ Slog.v(TAG, "No longer animating wallpaper targets!");
+ }
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+ mWallpaperTarget = foundW;
+ targetChanged = true;
+ }
+ }
+
+ boolean visible = foundW != null;
+ if (visible) {
+ // The window is visible to the compositor... but is it visible
+ // to the user? That is what the wallpaper cares about.
+ visible = isWallpaperVisible(foundW);
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
+
+ // If the wallpaper target is animating, we may need to copy
+ // its layer adjustment. Only do this if we are not transfering
+ // between two wallpaper targets.
+ mWallpaperAnimLayerAdjustment =
+ (mLowerWallpaperTarget == null && foundW.mAppToken != null)
+ ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0;
+
+ final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER)
+ + TYPE_LAYER_OFFSET;
+
+ // Now w is the window we are supposed to be behind... but we
+ // need to be sure to also be behind any of its attached windows,
+ // AND any starting window associated with it, AND below the
+ // maximum layer the policy allows for wallpapers.
+ while (foundI > 0) {
+ WindowState wb = windows.get(foundI - 1);
+ if (wb.mBaseLayer < maxLayer &&
+ wb.mAttachedWindow != foundW &&
+ (foundW.mAttachedWindow == null ||
+ wb.mAttachedWindow != foundW.mAttachedWindow) &&
+ (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
+ foundW.mToken == null || wb.mToken != foundW.mToken)) {
+ // This window is not related to the previous one in any
+ // interesting way, so stop here.
+ break;
+ }
+ foundW = wb;
+ foundI--;
+ }
+ } else {
+ if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target");
+ }
+
+ if (foundW == null && topCurW != null) {
+ // There is no wallpaper target, so it goes at the bottom.
+ // We will assume it is the same place as last time, if known.
+ foundW = topCurW;
+ foundI = topCurI+1;
+ } else {
+ // Okay i is the position immediately above the wallpaper. Look at
+ // what is below it for later.
+ foundW = foundI > 0 ? windows.get(foundI - 1) : null;
+ }
+
+ if (visible) {
+ if (mWallpaperTarget.mWallpaperX >= 0) {
+ mLastWallpaperX = mWallpaperTarget.mWallpaperX;
+ mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
+ }
+ if (mWallpaperTarget.mWallpaperY >= 0) {
+ mLastWallpaperY = mWallpaperTarget.mWallpaperY;
+ mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
+ }
+ if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
+ }
+ if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
+ }
+ }
+
+ // Start stepping backwards from here, ensuring that our wallpaper windows
+ // are correctly placed.
+ boolean changed = false;
+ for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+ WindowToken token = mWallpaperTokens.get(curTokenNdx);
+ if (token.hidden == visible) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
+ "Wallpaper token " + token + " hidden=" + !visible);
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the correct size.
+ mService.getDefaultDisplayContentLocked().layoutNeeded = true;
+ }
+
+ final WindowList tokenWindows = token.windows;
+ for (int wallpaperNdx = tokenWindows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+ WindowState wallpaper = tokenWindows.get(wallpaperNdx);
+
+ if (visible) {
+ updateWallpaperOffset(wallpaper, dw, dh, false);
+ }
+
+ // First, make sure the client has the current visibility state.
+ dispatchWallpaperVisibility(wallpaper, visible);
+
+ wallpaper.mWinAnimator.mAnimLayer =
+ wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
+
+ // First, if this window is at the current index, then all is well.
+ if (wallpaper == foundW) {
+ foundI--;
+ foundW = foundI > 0 ? windows.get(foundI - 1) : null;
+ continue;
+ }
+
+ // The window didn't match... the current wallpaper window,
+ // wherever it is, is in the wrong place, so make sure it is
+ // not in the list.
+ int oldIndex = windows.indexOf(wallpaper);
+ if (oldIndex >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
+ + oldIndex + ": " + wallpaper);
+ windows.remove(oldIndex);
+ mService.mWindowsChanged = true;
+ if (oldIndex < foundI) {
+ foundI--;
+ }
+ }
+
+ // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
+ // layer. For keyguard over wallpaper put the wallpaper under the keyguard.
+ int insertionIndex = 0;
+ if (visible && foundW != null) {
+ final int type = foundW.mAttrs.type;
+ final int privateFlags = foundW.mAttrs.privateFlags;
+ if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0
+ || type == TYPE_KEYGUARD_SCRIM) {
+ insertionIndex = windows.indexOf(foundW);
+ }
+ }
+ if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Moving wallpaper " + wallpaper
+ + " from " + oldIndex + " to " + insertionIndex);
+ }
+
+ windows.add(insertionIndex, wallpaper);
+ mService.mWindowsChanged = true;
+ changed = true;
+ }
+ }
+
+ if (targetChanged && DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target="
+ + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper="
+ + mUpperWallpaperTarget);
+
+ return changed;
+ }
+
+ boolean processWallpaperDrawPendingTimeout() {
+ if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
+ mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
+ if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
+ "*** WALLPAPER DRAW TIMEOUT");
+ return true;
+ }
+ return false;
+ }
+
+ boolean wallpaperTransitionReady() {
+ boolean transitionReady = true;
+ boolean wallpaperReady = true;
+ for (int curTokenIndex = mWallpaperTokens.size() - 1;
+ curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) {
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
+ curWallpaperIndex--) {
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
+ // We've told this wallpaper to be visible, but it is not drawn yet
+ wallpaperReady = false;
+ if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
+ // wait for this wallpaper until it is drawn or timeout
+ transitionReady = false;
+ }
+ if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
+ mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+ mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
+ WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+ }
+ if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
+ "Wallpaper should be visible but has not been drawn yet. " +
+ "mWallpaperDrawState=" + mWallpaperDrawState);
+ break;
+ }
+ }
+ }
+ if (wallpaperReady) {
+ mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+ }
+
+ return transitionReady;
+ }
+
+ void addWallpaperToken(WindowToken token) {
+ mWallpaperTokens.add(token);
+ }
+
+ void removeWallpaperToken(WindowToken token) {
+ mWallpaperTokens.remove(token);
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
+ if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) {
+ pw.print(prefix); pw.print("mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
+ pw.print(prefix); pw.print("mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
+ }
+ pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
+ pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
+ if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
+ || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ pw.print(prefix);
+ pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
+ pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
+ }
+ }
+
+ void dumpTokens(PrintWriter pw, String prefix, boolean dumpAll) {
+ if (!mWallpaperTokens.isEmpty()) {
+ pw.println();
+ pw.print(prefix); pw.println("Wallpaper tokens:");
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ WindowToken token = mWallpaperTokens.get(i);
+ pw.print(prefix); pw.print("Wallpaper #"); pw.print(i);
+ pw.print(' '); pw.print(token);
+ if (dumpAll) {
+ pw.println(':');
+ token.dump(pw, " ");
+ } else {
+ pw.println();
+ }
+ }
+ }
+ }
+}