/* * 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.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 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.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 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 = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM; final private WindowManagerService mService; private final ArrayList 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; private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); 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.isAnimationSet() && !mWallpaperTarget.mWinAnimator.isDummyAnimation(); } void updateWallpaperVisibility() { final DisplayContent displayContent = mWallpaperTarget.getDisplayContent(); if (displayContent == null) { return; } final boolean visible = isWallpaperVisible(mWallpaperTarget); 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("hideWallpapers"); 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.mShownPosition); // 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); } } } private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) { final WindowAnimator winAnimator = mService.mAnimator; result.reset(); WindowState w = null; int windowDetachedI = -1; boolean resetTopWallpaper = false; boolean inFreeformSpace = false; boolean replacing = false; for (int i = windows.size() - 1; i >= 0; i--) { w = windows.get(i); if ((w.mAttrs.type == TYPE_WALLPAPER)) { if (result.topWallpaper == null || resetTopWallpaper) { result.setTopWallpaper(w, i); resetTopWallpaper = false; } continue; } resetTopWallpaper = true; 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 (!inFreeformSpace) { TaskStack stack = w.getStack(); inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID; } replacing = replacing || w.mWillReplaceWindow; // If the app is executing an animation because the keyguard is going away (and the // keyguard was showing the wallpaper) 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.mKeyguardGoingAwayWithWallpaper); if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w); result.setWallpaperTarget(w, i); if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) { // The current wallpaper target is animating, so we'll look behind it for // another possible target and figure out what is going on later. if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": token animating, looking behind."); continue; } break; } else if (w == winAnimator.mWindowDetachedWallpaper) { windowDetachedI = i; } } if (result.wallpaperTarget == null && windowDetachedI >= 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w); result.setWallpaperTarget(w, windowDetachedI); } if (result.wallpaperTarget == null && (inFreeformSpace || (replacing && mWallpaperTarget != null))) { // In freeform mode we set the wallpaper as its own target, so we don't need an // additional window to make it visible. When we are replacing a window and there was // wallpaper before replacement, we want to keep the window until the new windows fully // appear and can determine the visibility, to avoid flickering. result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex); } } private boolean updateWallpaperWindowsTarget( WindowList windows, FindWallpaperTargetResult result) { boolean targetChanged = false; WindowState wallpaperTarget = result.wallpaperTarget; int wallpaperTargetIndex = result.wallpaperTargetIndex; if (mWallpaperTarget != wallpaperTarget && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != wallpaperTarget)) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "New wallpaper target: " + wallpaperTarget + " oldTarget: " + mWallpaperTarget); mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; WindowState oldW = mWallpaperTarget; mWallpaperTarget = wallpaperTarget; targetChanged = true; // Now what is happening... if the current and new targets are animating, // then we are in our super special mode! if (wallpaperTarget != null && oldW != null) { boolean oldAnim = oldW.isAnimatingLw(); boolean foundAnim = wallpaperTarget.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: " + wallpaperTargetIndex + " old i: " + oldI); if (oldI >= 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" + wallpaperTargetIndex + "=" + wallpaperTarget); // Set the new target correctly. if (wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.hiddenRequested) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target."); mWallpaperTarget = oldW; wallpaperTarget = oldW; wallpaperTargetIndex = oldI; } // Now set the upper and lower wallpaper targets correctly, // and make sure that we are positioning the wallpaper below the lower. else if (wallpaperTargetIndex > oldI) { // The new target is on top of the old one. if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Found target above old target."); mUpperWallpaperTarget = wallpaperTarget; mLowerWallpaperTarget = oldW; wallpaperTarget = oldW; wallpaperTargetIndex = 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 = wallpaperTarget; } } } } } 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 = wallpaperTarget; targetChanged = true; } } result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); return targetChanged; } boolean updateWallpaperWindowsTargetByLayer( WindowList windows, FindWallpaperTargetResult result) { WindowState wallpaperTarget = result.wallpaperTarget; int wallpaperTargetIndex = result.wallpaperTargetIndex; boolean visible = wallpaperTarget != 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(wallpaperTarget); 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 transferring between two wallpaper targets. mWallpaperAnimLayerAdjustment = (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null) ? wallpaperTarget.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 (wallpaperTargetIndex > 0) { WindowState wb = windows.get(wallpaperTargetIndex - 1); if (wb.mBaseLayer < maxLayer && wb.mAttachedWindow != wallpaperTarget && (wallpaperTarget.mAttachedWindow == null || wb.mAttachedWindow != wallpaperTarget.mAttachedWindow) && (wb.mAttrs.type != TYPE_APPLICATION_STARTING || wallpaperTarget.mToken == null || wb.mToken != wallpaperTarget.mToken)) { // This window is not related to the previous one in any // interesting way, so stop here. break; } wallpaperTarget = wb; wallpaperTargetIndex--; } } else { if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); } result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); return visible; } boolean updateWallpaperWindowsPlacement(WindowList windows, WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) { // TODO(multidisplay): Wallpapers on main screen only. final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo(); final int dw = displayInfo.logicalWidth; final int dh = displayInfo.logicalHeight; // 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 == wallpaperTarget) { wallpaperTargetIndex--; wallpaperTarget = wallpaperTargetIndex > 0 ? windows.get(wallpaperTargetIndex - 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 < wallpaperTargetIndex) { wallpaperTargetIndex--; } } // 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 && wallpaperTarget != null) { final int type = wallpaperTarget.mAttrs.type; final int privateFlags = wallpaperTarget.mAttrs.privateFlags; if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || type == TYPE_KEYGUARD_SCRIM) { insertionIndex = windows.indexOf(wallpaperTarget); } } if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG, "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex); windows.add(insertionIndex, wallpaper); mService.mWindowsChanged = true; changed = true; } } return changed; } boolean adjustWallpaperWindows() { mService.mWindowPlacerLocked.mWallpaperMayChange = false; final WindowList windows = mService.getDefaultWindowListLocked(); // First find top-most window that has asked to be on top of the wallpaper; // all wallpapers go behind it. findWallpaperTarget(windows, mFindResults); final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults); final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults); WindowState wallpaperTarget = mFindResults.wallpaperTarget; int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex; if (wallpaperTarget == null && mFindResults.topWallpaper != 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. wallpaperTarget = mFindResults.topWallpaper; wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1; } else { // Okay i is the position immediately above the wallpaper. // Look at what is below it for later. wallpaperTarget = wallpaperTargetIndex > 0 ? windows.get(wallpaperTargetIndex - 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; } } final boolean changed = updateWallpaperWindowsPlacement( windows, wallpaperTarget, wallpaperTargetIndex, visible); 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(); } } } } /** Helper class for storing the results of a wallpaper target find operation. */ final private static class FindWallpaperTargetResult { int topWallpaperIndex = 0; WindowState topWallpaper = null; int wallpaperTargetIndex = 0; WindowState wallpaperTarget = null; void setTopWallpaper(WindowState win, int index) { topWallpaper = win; topWallpaperIndex = index; } void setWallpaperTarget(WindowState win, int index) { wallpaperTarget = win; wallpaperTargetIndex = index; } void reset() { topWallpaperIndex = 0; topWallpaper = null; wallpaperTargetIndex = 0; wallpaperTarget = null; } } }