diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index c42647ed0aee9..e1b598a032040 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -411,7 +411,7 @@ public class AppWindowAnimator { } if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == mAppToken) { - mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */); + mAppToken.getDisplayContent().moveInputMethodWindowsIfNeeded(true); } if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 45695960853fb..5d739d13c898e 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1006,7 +1006,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree tStartingWindow.mToken = this; tStartingWindow.mAppToken = this; - if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, + "Removing starting window: " + tStartingWindow); + getDisplayContent().removeFromWindowList(tStartingWindow); + if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing starting " + tStartingWindow + " from " + fromToken); fromToken.removeChild(tStartingWindow); fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow); @@ -1256,6 +1259,18 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + int rebuildWindowListUnchecked(int addIndex) { + return super.rebuildWindowList(addIndex); + } + + @Override + int rebuildWindowList(int addIndex) { + if (mIsExiting && !waitingForReplacement()) { + return addIndex; + } + return rebuildWindowListUnchecked(addIndex); + } + @Override boolean forAllWindows(ToBooleanFunction callback, boolean traverseTopToBottom) { // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent @@ -1317,32 +1332,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mLastContainsShowWhenLockedWindow = containsShowWhenLocked; } - WindowState getImeTargetBelowWindow(WindowState w) { - final int index = mChildren.indexOf(w); - if (index > 0) { - final WindowState target = mChildren.get(index - 1); - if (target.canBeImeTarget()) { - return target; - } - } - return null; - } - - WindowState getHighestAnimLayerWindow(WindowState currentTarget) { - WindowState candidate = null; - for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) { - final WindowState w = mChildren.get(i); - if (w.mRemoved) { - continue; - } - if (candidate == null || w.mWinAnimator.mAnimLayer > - candidate.mWinAnimator.mAnimLayer) { - candidate = w; - } - } - return candidate; - } - @Override void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 24b9d6944ecc8..e73acde65a3d4 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -33,6 +33,10 @@ import static android.view.View.GONE; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; +import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; +import static android.view.WindowManager.INPUT_CONSUMER_PIP; +import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; @@ -40,10 +44,13 @@ import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -57,6 +64,7 @@ import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; @@ -69,8 +77,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +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.DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -84,6 +94,7 @@ import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; import static com.android.server.wm.WindowManagerService.dipToPixel; +import static com.android.server.wm.WindowManagerService.localLOGV; import static com.android.server.wm.WindowManagerService.logSurface; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; @@ -109,21 +120,29 @@ import android.util.DisplayMetrics; import android.util.Slog; import android.view.Display; import android.view.DisplayInfo; +import android.view.IWindow; +import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManagerPolicy; +import com.android.internal.util.FastPrintWriter; import com.android.internal.util.ToBooleanFunction; import com.android.internal.view.IInputMethodClient; +import com.android.server.input.InputWindowHandle; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; /** * Utility class for keeping track of the WindowStates and other pertinent contents of a @@ -155,7 +174,8 @@ class DisplayContent extends WindowContainer mTokenMap = new HashMap(); @@ -212,19 +232,21 @@ class DisplayContent extends WindowContainer mTapExcludedWindows = new ArrayList<>(); - private boolean mHaveBootMsg = false; - private boolean mHaveApp = false; - private boolean mHaveWallpaper = false; - private boolean mHaveKeyguard = true; + /** Used when rebuilding window list to keep track of windows that have been removed. */ + private WindowState[] mRebuildTmp = new WindowState[20]; + + /** + * Temporary list for comparison. Always clear this after use so we don't end up with + * orphaned windows references + */ + private final ArrayList mTmpWindows = new ArrayList<>(); private final LinkedList mTmpUpdateAllDrawn = new LinkedList(); private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult = new TaskForResizePointSearchResult(); - private final ApplySurfaceChangesTransactionState mTmpApplySurfaceChangesTransactionState = - new ApplySurfaceChangesTransactionState(); - private final ScreenshotApplicationState mScreenshotApplicationState = - new ScreenshotApplicationState(); + private final GetWindowOnDisplaySearchResult mTmpGetWindowOnDisplaySearchResult = + new GetWindowOnDisplaySearchResult(); // True if this display is in the process of being removed. Used to determine if the removal of // the display's direct children should be allowed. @@ -433,13 +455,17 @@ class DisplayContent extends WindowContainer= 0; --pos) { + final WindowState win = mWindows.get(pos); + if (win.mAppToken != null) { + // We hit an application window. so the orientation will be determined by the + // app window. No point in continuing further. + break; + } + if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) { + continue; + } + int req = win.mAttrs.screenOrientation; + if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) { + continue; + } + + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req); + if (policy.isKeyguardHostWindow(win.mAttrs)) { + mService.mLastKeyguardForcedOrientation = req; + } + return (mService.mLastWindowForcedOrientation = req); + } + mService.mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + + if (policy.isKeyguardShowingAndNotOccluded()) { + return mService.mLastKeyguardForcedOrientation; } } @@ -690,10 +738,22 @@ class DisplayContent extends WindowContainer= 0; --stackNdx) { + mTaskStackContainers.get(stackNdx).switchUser(); + } + + rebuildAppWindowList(); } private void resetAnimationBackgroundAnimator() { @@ -855,9 +915,19 @@ class DisplayContent extends WindowContainer { - w.adjustAnimLayer(adj); - }, true /* traverseTopToBottom */); + final WindowState imw = mService.mInputMethodWindow; + if (imw != null) { + imw.adjustAnimLayer(adj); + } + for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) { + final WindowState dialog = mService.mInputMethodDialogs.get(i); + // TODO: This and other places setting mAnimLayer can probably use WS.adjustAnimLayer, + // but need to make sure we are not setting things twice for child windows that are + // already in the list. + dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj; + if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw + + " anim layer: " + dialog.mWinAnimator.mAnimLayer); + } } /** @@ -866,11 +936,11 @@ class DisplayContent extends WindowContainer w.mIsWallpaper && w.isVisibleNow()); - - if (visibleWallpaper != null) { - return visibleWallpaper.mWinAnimator.mAnimLayer; + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState win = mWindows.get(i); + if (win.mIsWallpaper && win.isVisibleNow()) { + return win.mWinAnimator.mAnimLayer; + } } return winAnimator.mAnimLayer; } @@ -1018,37 +1088,48 @@ class DisplayContent extends WindowContainer { - final int flags = w.mAttrs.flags; - if (!w.isVisibleLw()) { - return false; + + for (int i = mWindows.size() - 1; i >= 0; i--) { + WindowState window = mWindows.get(i); + final int flags = window.mAttrs.flags; + if (!window.isVisibleLw()) { + continue; } if ((flags & FLAG_NOT_TOUCHABLE) != 0) { - return false; + continue; } - w.getVisibleBounds(mTmpRect); + window.getVisibleBounds(mTmpRect); if (!mTmpRect.contains(x, y)) { - return false; + continue; } - w.getTouchableRegion(mTmpRegion); + window.getTouchableRegion(mTmpRegion); final int touchFlags = flags & (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL); - return mTmpRegion.contains(x, y) || touchFlags == 0; - }); + if (mTmpRegion.contains(x, y) || touchFlags == 0) { + touchedWin = window; + break; + } + } return touchedWin; } boolean canAddToastWindowForUid(int uid) { // We allow one toast window per UID being shown at a time. - final WindowState win = getWindow(w -> - w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == uid && !w.mPermanentlyHidden - && !w.mWindowRemovalAllowed); - return win == null; + final int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + final WindowState window = mWindows.get(i); + if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid + && !window.mPermanentlyHidden && !window.mWindowRemovalAllowed) { + return false; + } + } + return true; } void scheduleToastWindowsTimeoutIfNeededLocked(WindowState oldFocus, WindowState newFocus) { @@ -1056,76 +1137,268 @@ class DisplayContent extends WindowContainer { - if (w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == lostFocusUid) { - if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, w)) { - handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, w), - w.mAttrs.hideTimeoutMilliseconds); + for (int i = 0; i < windowCount; i++) { + final WindowState window = mWindows.get(i); + if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == lostFocusUid) { + if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, window)) { + handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, window), + window.mAttrs.hideTimeoutMilliseconds); } } - }, false /* traverseTopToBottom */); + } } WindowState findFocusedWindow() { final AppWindowToken focusedApp = mService.mFocusedApp; - mTmpWindow = null; - forAllWindows(w -> { - if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + w - + ", flags=" + w.mAttrs.flags + ", canReceive=" + w.canReceiveKeys()); + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState win = mWindows.get(i); - if (!w.canReceiveKeys()) { - return false; + if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + i + " = " + win + + ", flags=" + win.mAttrs.flags + ", canReceive=" + win.canReceiveKeys()); + + if (!win.canReceiveKeys()) { + continue; } - final AppWindowToken wtoken = w.mAppToken; + final AppWindowToken wtoken = win.mAppToken; // If this window's application has been removed, just skip it. if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) { if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because " + (wtoken.removed ? "removed" : "sendingToBottom")); - return false; + continue; } if (focusedApp == null) { if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp=null" - + " using new focus @ " + w); - mTmpWindow = w; - return true; + + " using new focus @ " + i + " = " + win); + return win; } if (!focusedApp.windowsAreFocusable()) { // Current focused app windows aren't focusable... if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp windows not" - + " focusable using new focus @ " + w); - mTmpWindow = w; - return true; + + " focusable using new focus @ " + i + " = " + win); + return win; } // Descend through all of the app tokens and find the first that either matches // win.mAppToken (return win) or mFocusedApp (return null). - if (wtoken != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) { + if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING) { if (focusedApp.compareTo(wtoken) > 0) { // App stack below focused app stack. No focus for you!!! if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Reached focused app=" + focusedApp); - mTmpWindow = null; - return true; + return null; } } - if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + w); - mTmpWindow = w; - return true; - }, true /* traverseTopToBottom */); - - if (mTmpWindow == null) { - if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows."); - return null; + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + + i + " = " + win); + return win; + } + + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows."); + return null; + } + + void addAppWindowToWindowList(final WindowState win) { + final IWindow client = win.mClient; + + WindowList tokenWindowList = getTokenWindowsOnDisplay(win.mToken); + if (!tokenWindowList.isEmpty()) { + addAppWindowExisting(win, tokenWindowList); + return; + } + + // No windows from this token on this display + if (localLOGV) Slog.v(TAG_WM, "Figuring out where to add app window " + + client.asBinder() + " (token=" + this + ")"); + + final WindowToken wToken = win.mToken; + + // Figure out where the window should go, based on the order of applications. + mTmpGetWindowOnDisplaySearchResult.reset(); + for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { + final TaskStack stack = mTaskStackContainers.get(i); + stack.getWindowOnDisplayBeforeToken(this, wToken, mTmpGetWindowOnDisplaySearchResult); + if (mTmpGetWindowOnDisplaySearchResult.reachedToken) { + // We have reach the token we are interested in. End search. + break; + } + } + + WindowState pos = mTmpGetWindowOnDisplaySearchResult.foundWindow; + + // We now know the index into the apps. If we found an app window above, that gives us the + // position; else we need to look some more. + if (pos != null) { + // Move behind any windows attached to this one. + final WindowToken atoken = getWindowToken(pos.mClient.asBinder()); + if (atoken != null) { + tokenWindowList = getTokenWindowsOnDisplay(atoken); + final int NC = tokenWindowList.size(); + if (NC > 0) { + WindowState bottom = tokenWindowList.get(0); + if (bottom.mSubLayer < 0) { + pos = bottom; + } + } + } + addWindowToListBefore(win, pos); + return; + } + + // Continue looking down until we find the first token that has windows on this display. + mTmpGetWindowOnDisplaySearchResult.reset(); + for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { + final TaskStack stack = mTaskStackContainers.get(i); + stack.getWindowOnDisplayAfterToken(this, wToken, mTmpGetWindowOnDisplaySearchResult); + if (mTmpGetWindowOnDisplaySearchResult.foundWindow != null) { + // We have found a window after the token. End search. + break; + } + } + + pos = mTmpGetWindowOnDisplaySearchResult.foundWindow; + + if (pos != null) { + // Move in front of any windows attached to this one. + final WindowToken atoken = getWindowToken(pos.mClient.asBinder()); + if (atoken != null) { + final WindowState top = atoken.getTopWindow(); + if (top != null && top.mSubLayer >= 0) { + pos = top; + } + } + addWindowToListAfter(win, pos); + return; + } + + // Just search for the start of this layer. + final int myLayer = win.mBaseLayer; + int i; + for (i = mWindows.size() - 1; i >= 0; --i) { + final WindowState w = mWindows.get(i); + // Dock divider shares the base layer with application windows, but we want to always + // keep it above the application windows. The sharing of the base layer is intended + // for window animations, which need to be above the dock divider for the duration + // of the animation. + if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) { + break; + } + } + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, + "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + + mWindows.size()); + mWindows.add(i + 1, win); + mService.mWindowsChanged = true; + } + + /** Adds this non-app window to the window list. */ + void addNonAppWindowToWindowList(WindowState win) { + // Figure out where window should go, based on layer. + int i; + for (i = mWindows.size() - 1; i >= 0; i--) { + final WindowState otherWin = mWindows.get(i); + if (otherWin.getBaseType() != TYPE_WALLPAPER && otherWin.mBaseLayer <= win.mBaseLayer) { + // Wallpaper wanders through the window list, for example to position itself + // directly behind keyguard. Because of this it will break the ordering based on + // WindowState.mBaseLayer. There might windows with higher mBaseLayer behind it and + // we don't want the new window to appear above them. An example of this is adding + // of the docked stack divider. Consider a scenario with the following ordering (top + // to bottom): keyguard, wallpaper, assist preview, apps. We want the dock divider + // to land below the assist preview, so the dock divider must ignore the wallpaper, + // with which it shares the base layer. + break; + } + } + + i++; + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, + "Free window: Adding window " + this + " at " + i + " of " + mWindows.size()); + mWindows.add(i, win); + mService.mWindowsChanged = true; + } + + void addToWindowList(WindowState win, int index) { + mService.mWindowsChanged = true; + mWindows.add(index, win); + } + + boolean removeFromWindowList(WindowState win) { + mService.mWindowsChanged = true; + return mWindows.remove(win); + } + + private int removeWindowAndChildrenFromWindowList(WindowState win, int interestingPos) { + int wpos = mWindows.indexOf(win); + if (wpos < 0) { + return interestingPos; + } + + if (wpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this); + mWindows.remove(wpos); + mService.mWindowsChanged = true; + int childWinCount = win.mChildren.size(); + while (childWinCount > 0) { + childWinCount--; + final WindowState cw = win.mChildren.get(childWinCount); + int cpos = mWindows.indexOf(cw); + if (cpos >= 0) { + if (cpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, + "Temp removing child at " + cpos + ": " + cw); + mWindows.remove(cpos); + } + } + return interestingPos; + } + + void addChildWindowToWindowList(WindowState win) { + final WindowState parentWindow = win.getParentWindow(); + + WindowList windowsOnSameDisplay = getTokenWindowsOnDisplay(win.mToken); + + // Figure out this window's ordering relative to the parent window. + final int wCount = windowsOnSameDisplay.size(); + final int sublayer = win.mSubLayer; + int largestSublayer = Integer.MIN_VALUE; + WindowState windowWithLargestSublayer = null; + int i; + for (i = 0; i < wCount; i++) { + WindowState w = windowsOnSameDisplay.get(i); + final int wSublayer = w.mSubLayer; + if (wSublayer >= largestSublayer) { + largestSublayer = wSublayer; + windowWithLargestSublayer = w; + } + if (sublayer < 0) { + // For negative sublayers, we go below all windows in the same sublayer. + if (wSublayer >= sublayer) { + addWindowToListBefore(win, wSublayer >= 0 ? parentWindow : w); + break; + } + } else { + // For positive sublayers, we go above all windows in the same sublayer. + if (wSublayer > sublayer) { + addWindowToListBefore(win, w); + break; + } + } + } + if (i >= wCount) { + if (sublayer < 0) { + addWindowToListBefore(win, parentWindow); + } else { + addWindowToListAfter(win, + largestSublayer >= 0 ? windowWithLargestSublayer : parentWindow); + } } - return mTmpWindow; } /** Updates the layer assignment of windows on this display. */ @@ -1136,9 +1409,136 @@ class DisplayContent extends WindowContainer + *
  • Any windows that are currently below the wallpaper window stay below the wallpaper + * window. + *
  • Exiting application windows are at the bottom, but above the wallpaper window. + *
  • All other application windows are above the exiting application windows and ordered based + * on the ordering of their stacks and tasks on the display. + *
  • Non-application windows are at the very top. + * + *

    + * NOTE: This isn't a complete picture of what the user see. Further manipulation of the window + * surface layering is done in {@link WindowLayersController}. + */ + void rebuildAppWindowList() { + int count = mWindows.size(); + int i; + int lastBelow = -1; + int numRemoved = 0; + + if (mRebuildTmp.length < count) { + mRebuildTmp = new WindowState[count + 10]; + } + + // First remove all existing app windows. + i = 0; + while (i < count) { + final WindowState w = mWindows.get(i); + if (w.mAppToken != null) { + final WindowState win = mWindows.remove(i); + win.mRebuilding = true; + mRebuildTmp[numRemoved] = win; + mService.mWindowsChanged = true; + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Rebuild removing window: " + win); + count--; + numRemoved++; + continue; + } else if (lastBelow == i-1) { + if (w.mAttrs.type == TYPE_WALLPAPER) { + lastBelow = i; + } + } + i++; + } + + // Keep whatever windows were below the app windows still below, by skipping them. + lastBelow++; + i = lastBelow; + + // First add all of the exiting app tokens... these are no longer in the main app list, + // but still have windows shown. We put them in the back because now that the animation is + // over we no longer will care about them. + final int numStacks = mTaskStackContainers.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + AppTokenList exitingAppTokens = mTaskStackContainers.get(stackNdx).mExitingAppTokens; + int NT = exitingAppTokens.size(); + for (int j = 0; j < NT; j++) { + i = exitingAppTokens.get(j).rebuildWindowListUnchecked(i); + } + } + + // And add in the still active app tokens in Z order. + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + i = mTaskStackContainers.get(stackNdx).rebuildWindowList(i); + } + + i -= lastBelow; + if (i != numRemoved) { + setLayoutNeeded(); + Slog.w(TAG_WM, "On display=" + mDisplayId + " Rebuild removed " + numRemoved + + " windows but added " + i + " rebuildAppWindowListLocked() " + + " callers=" + Debug.getCallers(10)); + for (i = 0; i < numRemoved; i++) { + WindowState ws = mRebuildTmp[i]; + if (ws.mRebuilding) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + ws.dump(pw, "", true); + pw.flush(); + Slog.w(TAG_WM, "This window was lost: " + ws); + Slog.w(TAG_WM, sw.toString()); + ws.mWinAnimator.destroySurfaceLocked(); + } + } + Slog.w(TAG_WM, "Current window hierarchy:"); + dumpChildrenNames(); + Slog.w(TAG_WM, "Final window list:"); + dumpWindows(); + } + Arrays.fill(mRebuildTmp, null); + } + + /** Rebuilds the display's window list and does a relayout if something changed. */ + void rebuildAppWindowsAndLayoutIfNeeded() { + mTmpWindows.clear(); + mTmpWindows.addAll(mWindows); + + rebuildAppWindowList(); + + // Set displayContent.mLayoutNeeded if window order changed. + final int tmpSize = mTmpWindows.size(); + final int winSize = mWindows.size(); + int tmpNdx = 0, winNdx = 0; + while (tmpNdx < tmpSize && winNdx < winSize) { + // Skip over all exiting windows, they've been moved out of order. + WindowState tmp; + do { + tmp = mTmpWindows.get(tmpNdx++); + } while (tmpNdx < tmpSize && tmp.mAppToken != null && tmp.mAppToken.mIsExiting); + + WindowState win; + do { + win = mWindows.get(winNdx++); + } while (winNdx < winSize && win.mAppToken != null && win.mAppToken.mIsExiting); + + if (tmp != win) { + // Window order changed. + setLayoutNeeded(); + break; + } + } + if (tmpNdx != winNdx) { + // One list was different from the other. + setLayoutNeeded(); + } + mTmpWindows.clear(); if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/)) { @@ -1150,69 +1550,321 @@ class DisplayContent extends WindowContainer= 0; --winNdx) { + final WindowState child = mWindows.get(winNdx); + final InputChannel inputChannel = child.mInputChannel; + final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; + if (inputChannel == null || inputWindowHandle == null || child.mRemoved + || child.isAdjustedForMinimizedDock()) { + // Skip this window because it cannot possibly receive input. + continue; + } + + if (addPipInputConsumerHandle + && child.getStackId() == PINNED_STACK_ID + && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) { + // Update the bounds of the Pip input consumer to match the Pinned stack + child.getStack().getBounds(pipTouchableBounds); + pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds); + inputMonitor.addInputWindowHandle(pipInputConsumer.mWindowHandle); + addPipInputConsumerHandle = false; + } + + if (addInputConsumerHandle + && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) { + inputMonitor.addInputWindowHandle(navInputConsumer.mWindowHandle); + addInputConsumerHandle = false; + } + + if (addWallpaperInputConsumerHandle) { + if (child.mAttrs.type == TYPE_WALLPAPER && child.isVisibleLw()) { + // Add the wallpaper input consumer above the first visible wallpaper. + inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle); + addWallpaperInputConsumerHandle = false; + } + } + + final int flags = child.mAttrs.flags; + final int privateFlags = child.mAttrs.privateFlags; + final int type = child.mAttrs.type; + + final boolean hasFocus = child == inputFocus; + final boolean isVisible = child.isVisibleLw(); + if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { + disableWallpaperTouchEvents = true; + } + final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(child) + && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0 + && !disableWallpaperTouchEvents; + + // If there's a drag in progress and 'child' is a potential drop target, + // make sure it's been told about the drag + if (inDrag && isVisible && isDefaultDisplay) { + mService.mDragState.sendDragStartedIfNeededLw(child); + } + + inputMonitor.addInputWindowHandle( + inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper); + } + + if (addWallpaperInputConsumerHandle) { + // No visible wallpaper found, add the wallpaper input consumer at the end. + inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle); + } + } + /** Returns true if a leaked surface was destroyed */ boolean destroyLeakedSurfaces() { - // Used to indicate that a surface was leaked. - mTmpWindow = null; - forAllWindows(w -> { - final WindowStateAnimator wsa = w.mWinAnimator; + boolean leakedSurface = false; + final int numWindows = mWindows.size(); + for (int winNdx = 0; winNdx < numWindows; ++winNdx) { + final WindowState ws = mWindows.get(winNdx); + final WindowStateAnimator wsa = ws.mWinAnimator; if (wsa.mSurfaceController == null) { - return; + continue; } if (!mService.mSessions.contains(wsa.mSession)) { Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): " - + w + " surface=" + wsa.mSurfaceController - + " token=" + w.mToken - + " pid=" + w.mSession.mPid - + " uid=" + w.mSession.mUid); + + ws + " surface=" + wsa.mSurfaceController + + " token=" + ws.mToken + + " pid=" + ws.mSession.mPid + + " uid=" + ws.mSession.mUid); wsa.destroySurface(); - mService.mForceRemoves.add(w); - mTmpWindow = w; - } else if (w.mAppToken != null && w.mAppToken.clientHidden) { + mService.mForceRemoves.add(ws); + leakedSurface = true; + } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) { Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): " - + w + " surface=" + wsa.mSurfaceController - + " token=" + w.mAppToken - + " saved=" + w.hasSavedSurface()); - if (SHOW_TRANSACTIONS) logSurface(w, "LEAK DESTROY", false); + + ws + " surface=" + wsa.mSurfaceController + + " token=" + ws.mAppToken + + " saved=" + ws.hasSavedSurface()); + if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false); wsa.destroySurface(); - mTmpWindow = w; - } - }, false /* traverseTopToBottom */); - - return mTmpWindow != null; - } - - /** - * Determine and return the window that should be the IME target. - * @param updateImeTarget If true the system IME target will be updated to match what we found. - * @return The window that should be used as the IME target or null if there isn't any. - */ - WindowState computeImeTarget(boolean updateImeTarget) { - // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the - // same display. Or even when the current IME/target are not on the same screen as the next - // IME/target. For now only look for input windows on the main screen. - WindowState target = getWindow(w -> { - if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.i(TAG_WM, "Checking window @" - + w + " fl=0x" + Integer.toHexString(w.mAttrs.flags)); - return w.canBeImeTarget(); - }); - - - // Yet more tricksyness! If this window is a "starting" window, we do actually want - // to be on top of it, but it is not -really- where input will go. So look down below - // for a real window to target... - if (target != null && target.mAttrs.type == TYPE_APPLICATION_STARTING) { - final AppWindowToken token = target.mAppToken; - if (token != null) { - final WindowState betterTarget = token.getImeTargetBelowWindow(target); - if (betterTarget != null) { - target = betterTarget; - } + leakedSurface = true; } } - if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.v(TAG_WM, - "Proposed new IME target: " + target); + return leakedSurface; + } + + /** Return the list of Windows on this display associated with the input token. */ + WindowList getTokenWindowsOnDisplay(WindowToken token) { + final WindowList windowList = new WindowList(); + final int count = mWindows.size(); + for (int i = 0; i < count; i++) { + final WindowState win = mWindows.get(i); + if (win.mToken == token) { + windowList.add(win); + } + } + return windowList; + } + + private void reAddToWindowList(WindowState win) { + win.mToken.addWindow(win); + // This is a hack to get all of the child windows added as well at the right position. Child + // windows should be rare and this case should be rare, so it shouldn't be that big a deal. + int wpos = mWindows.indexOf(win); + if (wpos >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win); + mWindows.remove(wpos); + mService.mWindowsChanged = true; + win.reAddWindow(wpos); + } + } + + void moveInputMethodDialogs(int pos) { + ArrayList dialogs = mService.mInputMethodDialogs; + + final int N = dialogs.size(); + if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Removing " + N + " dialogs w/pos=" + pos); + for (int i = 0; i < N; i++) { + pos = removeWindowAndChildrenFromWindowList(dialogs.get(i), pos); + } + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "Window list w/pos=" + pos); + logWindowList(mWindows, " "); + } + + WindowState ime = mService.mInputMethodWindow; + if (pos >= 0) { + // Skip windows owned by the input method. + if (ime != null) { + while (pos < mWindows.size()) { + WindowState wp = mWindows.get(pos); + if (wp == ime || wp.getParentWindow() == ime) { + pos++; + continue; + } + break; + } + } + if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Adding " + N + " dialogs at pos=" + pos); + for (int i=0; i= 0) { + // In this case, the input method windows are to be placed + // immediately above the window they are targeting. + + // First check to see if the input method windows are already + // located here, and contiguous. + final int N = mWindows.size(); + final WindowState firstImWin = imPos < N ? mWindows.get(imPos) : null; + + // Figure out the actual input method window that should be + // at the bottom of their stack. + WindowState baseImWin = imWin != null ? imWin : mService.mInputMethodDialogs.get(0); + final WindowState cw = baseImWin.getBottomChild(); + if (cw != null && cw.mSubLayer < 0) { + baseImWin = cw; + } + + if (firstImWin == baseImWin) { + // The windows haven't moved... but are they still contiguous? + // First find the top IM window. + int pos = imPos+1; + while (pos < N) { + if (!(mWindows.get(pos)).mIsImWindow) { + break; + } + pos++; + } + pos++; + // Now there should be no more input method windows above. + while (pos < N) { + if ((mWindows.get(pos)).mIsImWindow) { + break; + } + pos++; + } + if (pos >= N) { + return false; + } + } + + if (imWin != null) { + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "Moving IM from " + imPos); + logWindowList(mWindows, " "); + } + imPos = removeWindowAndChildrenFromWindowList(imWin, imPos); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":"); + logWindowList(mWindows, " "); + } + imWin.reAddWindow(imPos); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "List after moving IM to " + imPos + ":"); + logWindowList(mWindows, " "); + } + if (DN > 0) moveInputMethodDialogs(imPos+1); + } else { + moveInputMethodDialogs(imPos); + } + + } else { + // In this case, the input method windows go in a fixed layer, + // because they aren't currently associated with a focus window. + + if (imWin != null) { + if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos); + removeWindowAndChildrenFromWindowList(imWin, 0); + reAddToWindowList(imWin); + if (DEBUG_INPUT_METHOD) { + Slog.v(TAG_WM, "List with no IM target:"); + logWindowList(mWindows, " "); + } + if (DN > 0) moveInputMethodDialogs(-1); + } else { + moveInputMethodDialogs(-1); + } + + } + + if (needAssignLayers) { + assignWindowLayers(false /* setLayoutNeeded */); + } + + return true; + } + + /** + * Dig through the WindowStates and find the one that the Input Method will target. + * @param willMove + * @return The index+1 in mWindows of the discovered target. + */ + int findDesiredInputMethodWindowIndex(boolean willMove) { + // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the + // same display. Or even when the current IME/target are not on the same screen as the next + // IME/target. For now only look for input windows on the main screen. + WindowState w = null; + int i; + for (i = mWindows.size() - 1; i >= 0; --i) { + final WindowState win = mWindows.get(i); + + if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i + + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags)); + if (canBeImeTarget(win)) { + w = win; + //Slog.i(TAG_WM, "Putting input method here!"); + + // Yet more tricksyness! If this window is a "starting" window, we do actually want + // to be on top of it, but it is not -really- where input will go. So if the caller + // is not actually looking to move the IME, look down below for a real window to + // target... + if (!willMove && w.mAttrs.type == TYPE_APPLICATION_STARTING && i > 0) { + final WindowState wb = mWindows.get(i-1); + if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { + i--; + w = wb; + } + } + break; + } + } + + // Now w is either mWindows[0] or an IME (or null if mWindows is empty). + + if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG_WM, "Proposed new IME target: " + w); // Now, a special case -- if the last target's window is in the process of exiting, and is // above the new target, keep on the last target to avoid flicker. Consider for example a @@ -1220,28 +1872,18 @@ class DisplayContent extends WindowContainer target.mWinAnimator.mAnimLayer)) { + if (curTarget != null + && curTarget.isDisplayedLw() + && curTarget.isClosing() + && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing"); - return curTarget; + return mWindows.indexOf(curTarget) + 1; } - if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target=" + target - + " updateImeTarget=" + updateImeTarget); + if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target=" + + w + " willMove=" + willMove); - if (target == null) { - if (updateImeTarget) { - if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget - + " to null." + (SHOW_STACK_CRAWLS ? " Callers=" - + Debug.getCallers(4) : "")); - setInputMethodTarget(null, mService.mInputMethodTargetWaitingAnim, 0); - } - - return null; - } - - if (updateImeTarget) { + if (willMove && w != null) { AppWindowToken token = curTarget == null ? null : curTarget.mAppToken; if (token != null) { @@ -1249,8 +1891,24 @@ class DisplayContent extends WindowContainer= 0) { + WindowState win = curWindows.get(pos); + if (win.mAppToken != token) { + break; + } + if (!win.mRemoved) { + if (highestTarget == null || win.mWinAnimator.mAnimLayer > + highestTarget.mWinAnimator.mAnimLayer) { + highestTarget = win; + highestPos = pos; + } + } + pos--; + } } if (highestTarget != null) { @@ -1258,76 +1916,121 @@ class DisplayContent extends WindowContainer { - if (w == top) { - // Reached the first window in the range we are interested in. - mTmpWindow = w; - } - if (mTmpWindow == null) { - return false; + //Slog.i(TAG_WM, "Placing input method @" + (i+1)); + if (w != null) { + if (willMove) { + if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to " + + w + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : "")); + mService.mInputMethodTarget = w; + mService.mInputMethodTargetWaitingAnim = false; + if (w.mAppToken != null) { + setInputMethodAnimLayerAdjustment( + w.mAppToken.mAppAnimator.animLayerAdjustment); + } else { + setInputMethodAnimLayerAdjustment(0); + } } - if (w.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) { - return true; + // If the docked divider is visible, we still need to go through this whole excercise to + // find the appropriate input method target (used for animations and dialog + // adjustments), but for purposes of Z ordering we simply wish to place it above the + // docked divider. Unless it is already above the divider. + final WindowState dockedDivider = mDividerControllerLocked.getWindow(); + if (dockedDivider != null && dockedDivider.isVisibleLw()) { + int dividerIndex = mWindows.indexOf(dockedDivider); + if (dividerIndex > 0 && dividerIndex > i) { + return dividerIndex + 1; + } + } + return i+1; + } + if (willMove) { + if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + + " to null." + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : "")); + mService.mInputMethodTarget = null; + setInputMethodAnimLayerAdjustment(0); + } + return -1; + } + + private static boolean canBeImeTarget(WindowState w) { + final int fl = w.mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); + final int type = w.mAttrs.type; + + if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM) + && type != TYPE_APPLICATION_STARTING) { + return false; + } + + if (DEBUG_INPUT_METHOD) { + Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); + if (!w.isVisibleOrAdding()) { + Slog.i(TAG_WM, " mSurfaceController=" + w.mWinAnimator.mSurfaceController + + " relayoutCalled=" + w.mRelayoutCalled + + " viewVis=" + w.mViewVisibility + + " policyVis=" + w.mPolicyVisibility + + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim + + " parentHidden=" + w.isParentWindowHidden() + + " exiting=" + w.mAnimatingExit + " destroying=" + w.mDestroying); + if (w.mAppToken != null) { + Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + w.mAppToken.hiddenRequested); + } + } + } + return w.isVisibleOrAdding(); + } + + private void logWindowList(final WindowList windows, String prefix) { + int N = windows.size(); + while (N > 0) { + N--; + Slog.v(TAG_WM, prefix + "#" + N + ": " + windows.get(N)); + } + } + + boolean getNeedsMenu(WindowState win, WindowManagerPolicy.WindowState bottom) { + int index = -1; + while (true) { + if (win.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) { + return win.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE; } // If we reached the bottom of the range of windows we are considering, // assume no menu is needed. - if (w == bottom) { - return true; + if (win == bottom) { + return false; } - return false; - }); - - return candidate != null && candidate.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE; + // The current window hasn't specified whether menu key is needed; look behind it. + // First, we may need to determine the starting position. + if (index < 0) { + index = mWindows.indexOf(win); + } + index--; + if (index < 0) { + return false; + } + win = mWindows.get(index); + } } void setLayoutNeeded() { @@ -1344,6 +2047,85 @@ class DisplayContent extends WindowContainer= 0; j--) { + final WindowState wentry = mWindows.get(j); + if(wentry.mAppToken == win.mAppToken) { + return j; + } + } + return -1; + } + + private void dumpChildrenNames() { + StringBuilder output = new StringBuilder(); + dumpChildrenNames(output, " "); + Slog.v(TAG_WM, output.toString()); + } + + private void dumpWindows() { + Slog.v(TAG_WM, " Display #" + mDisplayId); + for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) { + Slog.v(TAG_WM, " #" + winNdx + ": " + mWindows.get(winNdx)); + } + } + void dumpTokens(PrintWriter pw, boolean dumpAll) { if (mTokenMap.isEmpty()) { return; @@ -1364,24 +2146,25 @@ class DisplayContent extends WindowContainer { - final WindowStateAnimator wAnim = w.mWinAnimator; - pw.println(subPrefix + "Window #" + index[0] + ": " + wAnim); - index[0] = index[0] + 1; - }, false /* traverseTopToBottom */); + final int count = mWindows.size(); + for (int j = 0; j < count; j++) { + final WindowStateAnimator wAnim = mWindows.get(j).mWinAnimator; + pw.println(subPrefix + "Window #" + j + ": " + wAnim); + } } void enableSurfaceTrace(FileDescriptor fd) { - forAllWindows(w -> { - w.mWinAnimator.enableSurfaceTrace(fd); - }, true /* traverseTopToBottom */); + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState win = mWindows.get(i); + win.mWinAnimator.enableSurfaceTrace(fd); + } } void disableSurfaceTrace() { - forAllWindows(w -> { - w.mWinAnimator.disableSurfaceTrace(); - }, true /* traverseTopToBottom */); + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState win = mWindows.get(i); + win.mWinAnimator.disableSurfaceTrace(); + } } /** @@ -1389,68 +2172,63 @@ class DisplayContent extends WindowContainer { - if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)) { - w.mWinAnimator.setAnimation( + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState window = mWindows.get(i); + if (window.mAppToken == null && policy.canBeHiddenByKeyguardLw(window)) { + window.mWinAnimator.setAnimation( policy.createHiddenByKeyguardExit(onWallpaper, goingToShade)); } - }, true /* traverseTopToBottom */); + } } boolean checkWaitingForWindows() { - mHaveBootMsg = false; - mHaveApp = false; - mHaveWallpaper = false; - mHaveKeyguard = true; - - final WindowState visibleWindow = getWindow(w -> { + boolean haveBootMsg = false; + boolean haveApp = false; + // if the wallpaper service is disabled on the device, we're never going to have + // wallpaper, don't bother waiting for it + boolean haveWallpaper = false; + boolean wallpaperEnabled = mService.mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableWallpaperService) + && !mService.mOnlyCore; + boolean haveKeyguard = true; + final int count = mWindows.size(); + for (int i = 0; i < count; i++) { + final WindowState w = mWindows.get(i); if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) { return true; } if (w.isDrawnLw()) { if (w.mAttrs.type == TYPE_BOOT_PROGRESS) { - mHaveBootMsg = true; + haveBootMsg = true; } else if (w.mAttrs.type == TYPE_APPLICATION || w.mAttrs.type == TYPE_DRAWN_APPLICATION) { - mHaveApp = true; + haveApp = true; } else if (w.mAttrs.type == TYPE_WALLPAPER) { - mHaveWallpaper = true; + haveWallpaper = true; } else if (w.mAttrs.type == TYPE_STATUS_BAR) { - mHaveKeyguard = mService.mPolicy.isKeyguardDrawnLw(); + haveKeyguard = mService.mPolicy.isKeyguardDrawnLw(); } } - return false; - }); - - if (visibleWindow != null) { - // We have a visible window. - return true; } - // if the wallpaper service is disabled on the device, we're never going to have - // wallpaper, don't bother waiting for it - boolean wallpaperEnabled = mService.mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableWallpaperService) - && !mService.mOnlyCore; - if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******** booted=" + mService.mSystemBooted + " msg=" + mService.mShowingBootMessages - + " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp - + " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled - + " haveKeyguard=" + mHaveKeyguard); + + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp + + " haveWall=" + haveWallpaper + " wallEnabled=" + wallpaperEnabled + + " haveKeyguard=" + haveKeyguard); // If we are turning on the screen to show the boot message, don't do it until the boot // message is actually displayed. - if (!mService.mSystemBooted && !mHaveBootMsg) { + if (!mService.mSystemBooted && !haveBootMsg) { return true; } // If we are turning on the screen after the boot is completed normally, don't do so until // we have the application and wallpaper. - if (mService.mSystemBooted - && ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) { + if (mService.mSystemBooted && ((!haveApp && !haveKeyguard) || + (wallpaperEnabled && !haveWallpaper))) { return true; } @@ -1458,8 +2236,10 @@ class DisplayContent extends WindowContainer { - WindowStateAnimator winAnimator = w.mWinAnimator; + final WallpaperController wallpaperController = mWallpaperController; + for (int i = mWindows.size() - 1; i >= 0; i--) { + WindowState win = mWindows.get(i); + WindowStateAnimator winAnimator = win.mWinAnimator; if (winAnimator.hasSurface()) { final boolean wasAnimating = winAnimator.mWasAnimating; final boolean nowAnimating = winAnimator.stepAnimationLocked(animator.mCurrentTime); @@ -1467,10 +2247,10 @@ class DisplayContent extends WindowContainer { - w.mWinAnimator.prepareSurfaceLocked(true); - }, false /* traverseTopToBottom */); + final int count = mWindows.size(); + for (int j = 0; j < count; j++) { + mWindows.get(j).mWinAnimator.prepareSurfaceLocked(true); + } } boolean inputMethodClientHasFocus(IInputMethodClient client) { - final WindowState imFocus = computeImeTarget(false /* updateImeTarget */); - if (imFocus == null) { + // The focus for the client is the window immediately below where we would place the input + // method window. + int idx = findDesiredInputMethodWindowIndex(false); + if (idx <= 0) { return false; } + WindowState imFocus = mWindows.get(idx - 1); if (DEBUG_INPUT_METHOD) { Slog.i(TAG_WM, "Desired input method target: " + imFocus); Slog.i(TAG_WM, "Current focus: " + mService.mCurrentFocus); Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus); } + if (imFocus == null) { + return false; + } + + // This may be a starting window, in which case we still want to count it as okay. + if (imFocus.mAttrs.type == TYPE_APPLICATION_STARTING && imFocus.mAppToken != null) { + // The client has definitely started, so it really should have a window in this app + // token. Let's look for it. + final WindowState w = imFocus.mAppToken.getFirstNonStartingWindow(); + if (w != null) { + if (DEBUG_INPUT_METHOD) Slog.i(TAG_WM, "Switching to real app window: " + w); + imFocus = w; + } + } + final IInputMethodClient imeClient = imFocus.mSession.mClient; if (DEBUG_INPUT_METHOD) { @@ -1598,63 +2398,75 @@ class DisplayContent extends WindowContainer w.isOnScreen() && (w.mAttrs.flags & FLAG_SECURE) != 0); - return win != null; + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState ws = mWindows.get(i); + if (ws.isOnScreen() && (ws.mAttrs.flags & FLAG_SECURE) != 0) { + return true; + } + } + return false; } void updateSystemUiVisibility(int visibility, int globalDiff) { - forAllWindows(w -> { + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState ws = mWindows.get(i); try { - final int curValue = w.mSystemUiVisibility; - final int diff = (curValue ^ visibility) & globalDiff; - final int newValue = (curValue & ~diff) | (visibility & diff); + int curValue = ws.mSystemUiVisibility; + int diff = (curValue ^ visibility) & globalDiff; + int newValue = (curValue & ~diff) | (visibility & diff); if (newValue != curValue) { - w.mSeq++; - w.mSystemUiVisibility = newValue; + ws.mSeq++; + ws.mSystemUiVisibility = newValue; } - if (newValue != curValue || w.mAttrs.hasSystemUiListeners) { - w.mClient.dispatchSystemUiVisibilityChanged(w.mSeq, + if (newValue != curValue || ws.mAttrs.hasSystemUiListeners) { + ws.mClient.dispatchSystemUiVisibilityChanged(ws.mSeq, visibility, newValue, diff); } } catch (RemoteException e) { // so sorry } - }, true /* traverseTopToBottom */); + } } void onWindowFreezeTimeout() { Slog.w(TAG_WM, "Window freeze timeout expired."); mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; - - forAllWindows(w -> { + for (int i = mWindows.size() - 1; i >= 0; --i) { + final WindowState w = mWindows.get(i); if (!w.mOrientationChanging) { - return; + continue; } w.mOrientationChanging = false; w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime); Slog.w(TAG_WM, "Force clearing orientation change: " + w); - }, true /* traverseTopToBottom */); + } mService.mWindowPlacerLocked.performSurfacePlacement(); } void waitForAllWindowsDrawn() { final WindowManagerPolicy policy = mService.mPolicy; - forAllWindows(w -> { - final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs); - if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) { - w.mWinAnimator.mDrawState = DRAW_PENDING; + for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) { + final WindowState win = mWindows.get(winNdx); + final boolean keyguard = policy.isKeyguardHostWindow(win.mAttrs); + if (win.isVisibleLw() && (win.mAppToken != null || keyguard)) { + win.mWinAnimator.mDrawState = DRAW_PENDING; // Force add to mResizingWindows. - w.mLastContentInsets.set(-1, -1, -1, -1); - mService.mWaitingForDrawn.add(w); + win.mLastContentInsets.set(-1, -1, -1, -1); + mService.mWaitingForDrawn.add(win); } - }, true /* traverseTopToBottom */); + } } // TODO: Super crazy long method that should be broken down... boolean applySurfaceChangesTransaction(boolean recoveringMemory) { + boolean focusDisplayed = false; + boolean displayHasContent = false; + float preferredRefreshRate = 0; + int preferredModeId = 0; + + final int dw = mDisplayInfo.logicalWidth; final int dh = mDisplayInfo.logicalHeight; final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; @@ -1678,7 +2490,7 @@ class DisplayContent extends WindowContainer { + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState w = mWindows.get(i); mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow(), mService.mInputMethodTarget); - }, true /* traverseTopToBottom */); + } pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( "after finishPostLayoutPolicyLw", pendingLayoutChanges); } } while (pendingLayoutChanges != 0); - final RootWindowContainer root = mService.mRoot; - mTmpApplySurfaceChangesTransactionState.reset(); + RootWindowContainer root = mService.mRoot; + boolean obscured = false; + boolean syswin = false; resetDimming(); // Only used if default window final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty(); - forAllWindows(w -> { - final boolean obscuredChanged = w.mObscured != - mTmpApplySurfaceChangesTransactionState.obscured; + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState w = mWindows.get(i); + final Task task = w.getTask(); + final boolean obscuredChanged = w.mObscured != obscured; // Update effect. - w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured; - if (!mTmpApplySurfaceChangesTransactionState.obscured) { + w.mObscured = obscured; + if (!obscured) { final boolean isDisplayed = w.isDisplayedLw(); if (isDisplayed && w.isObscuringFullscreen(mDisplayInfo)) { // This window completely covers everything behind it, so we want to leave all // of them as undimmed (for performance reasons). root.mObscuringWindow = w; - mTmpApplySurfaceChangesTransactionState.obscured = true; + obscured = true; } - mTmpApplySurfaceChangesTransactionState.displayHasContent |= - root.handleNotObscuredLocked(w, - mTmpApplySurfaceChangesTransactionState.obscured, - mTmpApplySurfaceChangesTransactionState.syswin); + displayHasContent |= root.handleNotObscuredLocked(w, obscured, syswin); if (w.mHasSurface && isDisplayed) { final int type = w.mAttrs.type; if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR || (w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { - mTmpApplySurfaceChangesTransactionState.syswin = true; + syswin = true; } - if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0 - && w.mAttrs.preferredRefreshRate != 0) { - mTmpApplySurfaceChangesTransactionState.preferredRefreshRate - = w.mAttrs.preferredRefreshRate; + if (preferredRefreshRate == 0 && w.mAttrs.preferredRefreshRate != 0) { + preferredRefreshRate = w.mAttrs.preferredRefreshRate; } - if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0 - && w.mAttrs.preferredDisplayModeId != 0) { - mTmpApplySurfaceChangesTransactionState.preferredModeId - = w.mAttrs.preferredDisplayModeId; + if (preferredModeId == 0 && w.mAttrs.preferredDisplayModeId != 0) { + preferredModeId = w.mAttrs.preferredDisplayModeId; } } } @@ -1830,16 +2638,16 @@ class DisplayContent extends WindowContainer { + int topAttached = -1; + for (i = mWindows.size() - 1; i >= 0; i--) { + final WindowState win = mWindows.get(i); + // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid // wasting time and funky changes while a window is animating away. - final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) - || w.isGoneForLayoutLw(); + final boolean gone = (behindDream && mService.mPolicy.canBeHiddenByKeyguardLw(win)) + || win.isGoneForLayoutLw(); - if (DEBUG_LAYOUT && !w.mLayoutAttached) { - Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame - + " mLayoutAttached=" + w.mLayoutAttached - + " screen changed=" + w.isConfigChanged()); - final AppWindowToken atoken = w.mAppToken; - if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility - + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.hidden + if (DEBUG_LAYOUT && !win.mLayoutAttached) { + Slog.v(TAG, "1ST PASS " + win + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame + + " mLayoutAttached=" + win.mLayoutAttached + + " screen changed=" + win.isConfigChanged()); + final AppWindowToken atoken = win.mAppToken; + if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + win.mViewVisibility + + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested) - + " parentHidden=" + w.isParentWindowHidden()); - else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility - + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.hidden + + " parentHidden=" + win.isParentWindowHidden()); + else Slog.v(TAG, " VIS: mViewVisibility=" + win.mViewVisibility + + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested) - + " parentHidden=" + w.isParentWindowHidden()); + + " parentHidden=" + win.isParentWindowHidden()); } // If this view is GONE, then skip it -- keep the current frame, and let the caller know // so they can ignore it if they want. (We do the normal layout for INVISIBLE windows, // since that means "perform layout as normal, just don't display"). - if (!gone || !w.mHaveFrame || w.mLayoutNeeded - || ((w.isConfigChanged() || w.setReportResizeHints()) - && !w.isGoneForLayoutLw() && - ((w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || - (w.mHasSurface && w.mAppToken != null && - w.mAppToken.layoutConfigChanges)))) { - if (!w.mLayoutAttached) { + if (!gone || !win.mHaveFrame || win.mLayoutNeeded + || ((win.isConfigChanged() || win.setReportResizeHints()) + && !win.isGoneForLayoutLw() && + ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || + (win.mHasSurface && win.mAppToken != null && + win.mAppToken.layoutConfigChanges)))) { + if (!win.mLayoutAttached) { if (initial) { //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); - w.mContentChanged = false; + win.mContentChanged = false; } - if (w.mAttrs.type == TYPE_DREAM) { + if (win.mAttrs.type == TYPE_DREAM) { // Don't layout windows behind a dream, so that if it does stuff like hide // the status bar we won't get a bad transition when it goes away. - mTmpWindow = w; + behindDream = true; } - w.mLayoutNeeded = false; - w.prelayout(); - mService.mPolicy.layoutWindowLw(w, null); - w.mLayoutSeq = mService.mLayoutSeq; + win.mLayoutNeeded = false; + win.prelayout(); + mService.mPolicy.layoutWindowLw(win, null); + win.mLayoutSeq = seq; // Window frames may have changed. Update dim layer with the new bounds. - final Task task = w.getTask(); + final Task task = win.getTask(); if (task != null) { mDimLayerController.updateDimLayer(task); } - if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame - + " mContainingFrame=" + w.mContainingFrame - + " mDisplayFrame=" + w.mDisplayFrame); + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + + " mContainingFrame=" + win.mContainingFrame + + " mDisplayFrame=" + win.mDisplayFrame); + } else { + if (topAttached < 0) topAttached = i; } } - }, true /* traverseTopToBottom */); + } - // Used to indicate that we have processed the dream window and all additional attached - // windows are behind it. - final WindowState dreamWin = mTmpWindow; - mTmpWindow = null; + boolean attachedBehindDream = false; // Now perform layout of attached windows, which usually depend on the position of the // window they are attached to. XXX does not deal with windows that are attached to windows // that are themselves attached. - forAllWindows(w -> { - if (w.mLayoutAttached) { - if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame - + " mViewVisibility=" + w.mViewVisibility - + " mRelayoutCalled=" + w.mRelayoutCalled); - // If this view is GONE, then skip it -- keep the current frame, and let the - // caller know so they can ignore it if they want. (We do the normal layout for - // INVISIBLE windows, since that means "perform layout as normal, just don't - // display"). - if (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) { - return; + for (i = topAttached; i >= 0; i--) { + final WindowState win = mWindows.get(i); + + if (win.mLayoutAttached) { + if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame + + " mViewVisibility=" + win.mViewVisibility + + " mRelayoutCalled=" + win.mRelayoutCalled); + // If this view is GONE, then skip it -- keep the current frame, and let the caller + // know so they can ignore it if they want. (We do the normal layout for INVISIBLE + // windows, since that means "perform layout as normal, just don't display"). + if (attachedBehindDream && mService.mPolicy.canBeHiddenByKeyguardLw(win)) { + continue; } - if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame - || w.mLayoutNeeded) { + if ((win.mViewVisibility != GONE && win.mRelayoutCalled) || !win.mHaveFrame + || win.mLayoutNeeded) { if (initial) { //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); - w.mContentChanged = false; + win.mContentChanged = false; } - w.mLayoutNeeded = false; - w.prelayout(); - mService.mPolicy.layoutWindowLw(w, w.getParentWindow()); - w.mLayoutSeq = mService.mLayoutSeq; - if (DEBUG_LAYOUT) - Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame - + " mContainingFrame=" + w.mContainingFrame - + " mDisplayFrame=" + w.mDisplayFrame); + win.mLayoutNeeded = false; + win.prelayout(); + mService.mPolicy.layoutWindowLw(win, win.getParentWindow()); + win.mLayoutSeq = seq; + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + + " mContainingFrame=" + win.mContainingFrame + + " mDisplayFrame=" + win.mDisplayFrame); } - } else if (w.mAttrs.type == TYPE_DREAM) { + } else if (win.mAttrs.type == TYPE_DREAM) { // Don't layout windows behind a dream, so that if it does stuff like hide the // status bar we won't get a bad transition when it goes away. - mTmpWindow = dreamWin; + attachedBehindDream = behindDream; } - }, true /* traverseTopToBottom */); + } // Window frames may have changed. Tell the input dispatcher about it. mService.mInputMonitor.layoutInputConsumers(dw, dh); @@ -2011,7 +2819,7 @@ class DisplayContent extends WindowContainer { - if (!w.mHasSurface) { - return false; + appWin = null; + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState ws = mWindows.get(i); + if (!ws.mHasSurface) { + continue; } - if (w.mLayer >= aboveAppLayer) { - return false; + if (ws.mLayer >= aboveAppLayer) { + continue; } - if (wallpaperOnly && !w.mIsWallpaper) { - return false; + if (wallpaperOnly && !ws.mIsWallpaper) { + continue; } - if (w.mIsImWindow) { + if (ws.mIsImWindow) { if (!includeImeInScreenshot) { - return false; + continue; } - } else if (w.mIsWallpaper) { + } else if (ws.mIsWallpaper) { // If this is the wallpaper layer and we're only looking for the wallpaper layer // then the target window state is this one. if (wallpaperOnly) { - mScreenshotApplicationState.appWin = w; + appWin = ws; } - if (mScreenshotApplicationState.appWin == null) { + if (appWin == null) { // We have not ran across the target window yet, so it is probably behind // the wallpaper. This can happen when the keyguard is up and all windows // are moved behind the wallpaper. We don't want to include the wallpaper // layer in the screenshot as it will cover-up the layer of the target // window. - return false; + continue; } // Fall through. The target window is in front of the wallpaper. For this // case we want to include the wallpaper layer in the screenshot because // the target window might have some transparent areas. } else if (appToken != null) { - if (w.mAppToken == null || w.mAppToken.token != appToken) { + if (ws.mAppToken == null || ws.mAppToken.token != appToken) { // This app window is of no interest if it is not associated with the // screenshot app. - return false; + continue; } - mScreenshotApplicationState.appWin = w; + appWin = ws; } // Include this window. - final WindowStateAnimator winAnim = w.mWinAnimator; + final WindowStateAnimator winAnim = ws.mWinAnimator; int layer = winAnim.mSurfaceController.getLayer(); - if (mScreenshotApplicationState.maxLayer < layer) { - mScreenshotApplicationState.maxLayer = layer; + if (maxLayer < layer) { + maxLayer = layer; } - if (mScreenshotApplicationState.minLayer > layer) { - mScreenshotApplicationState.minLayer = layer; + if (minLayer > layer) { + minLayer = layer; } // Don't include wallpaper in bounds calculation - if (!includeFullDisplay && !w.mIsWallpaper) { - final Rect wf = w.mFrame; - final Rect cr = w.mContentInsets; + if (!includeFullDisplay && !ws.mIsWallpaper) { + final Rect wf = ws.mFrame; + final Rect cr = ws.mContentInsets; int left = wf.left + cr.left; int top = wf.top + cr.top; int right = wf.right - cr.right; int bottom = wf.bottom - cr.bottom; frame.union(left, top, right, bottom); - w.getVisibleBounds(stackBounds); + ws.getVisibleBounds(stackBounds); if (!Rect.intersects(frame, stackBounds)) { // Set frame empty if there's no intersection. frame.setEmpty(); @@ -2118,22 +2939,16 @@ class DisplayContent extends WindowContainer { - final InputChannel inputChannel = w.mInputChannel; - final InputWindowHandle inputWindowHandle = w.mInputWindowHandle; - if (inputChannel == null || inputWindowHandle == null || w.mRemoved - || w.isAdjustedForMinimizedDock()) { - // Skip this window because it cannot possibly receive input. - return; - } - - if (mAddPipInputConsumerHandle - && w.getStackId() == PINNED_STACK_ID - && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) { - // Update the bounds of the Pip input consumer to match the Pinned stack - w.getStack().getBounds(pipTouchableBounds); - pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds); - addInputWindowHandle(pipInputConsumer.mWindowHandle); - mAddPipInputConsumerHandle = false; - } - - if (mAddInputConsumerHandle - && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) { - addInputWindowHandle(navInputConsumer.mWindowHandle); - mAddInputConsumerHandle = false; - } - - if (mAddWallpaperInputConsumerHandle) { - if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) { - // Add the wallpaper input consumer above the first visible wallpaper. - addInputWindowHandle(wallpaperInputConsumer.mWindowHandle); - mAddWallpaperInputConsumerHandle = false; - } - } - - final int flags = w.mAttrs.flags; - final int privateFlags = w.mAttrs.privateFlags; - final int type = w.mAttrs.type; - - final boolean hasFocus = w == mInputFocus; - final boolean isVisible = w.isVisibleLw(); - if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { - mDisableWallpaperTouchEvents = true; - } - final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w) - && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0 - && !mDisableWallpaperTouchEvents; - - // If there's a drag in progress and 'child' is a potential drop target, - // make sure it's been told about the drag - if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) { - mService.mDragState.sendDragStartedIfNeededLw(w); - } - - addInputWindowHandle( - inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper); - }, true /* traverseTopToBottom */); - - if (mAddWallpaperInputConsumerHandle) { - // No visible wallpaper found, add the wallpaper input consumer at the end. - addInputWindowHandle(wallpaperInputConsumer.mWindowHandle); - } + mService.mRoot.updateInputWindows(this, mInputFocus, inDrag); // Send windows to native code. mService.mInputManager.setInputWindows(mInputWindowHandles); + + // Clear the list in preparation for the next round. + clearInputWindowHandlesLw(); + + if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw"); } /* Notifies that the input device configuration has changed. */ diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 1962ca7388920..88986e33894ae 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -219,7 +219,7 @@ class RootWindowContainer extends WindowContainer { return false; } - void getWindowsByName(ArrayList output, String name) { + void getWindowsByName(WindowList output, String name) { int objectId = 0; // See if this is an object ID. try { @@ -231,7 +231,7 @@ class RootWindowContainer extends WindowContainer { getWindowsByName(output, name, objectId); } - private void getWindowsByName(ArrayList output, String name, int objectId) { + private void getWindowsByName(WindowList output, String name, int objectId) { forAllWindows((w) -> { if (name != null) { if (w.mAttrs.getTitle().toString().contains(name)) { @@ -276,6 +276,15 @@ class RootWindowContainer extends WindowContainer { return null; } + // TODO: Users would have their own window containers under the display container? + void switchUser() { + final int count = mChildren.size(); + for (int i = 0; i < count; ++i) { + final DisplayContent dc = mChildren.get(i); + dc.switchUser(); + } + } + /** * Set new display override config and return array of ids of stacks that were changed during * update. If called for the default display, global configuration will also be updated. @@ -420,6 +429,14 @@ class RootWindowContainer extends WindowContainer { return hasChanges; } + void updateInputWindows(InputMonitor inputMonitor, WindowState inputFocus, boolean inDrag) { + final int count = mChildren.size(); + for (int i = 0; i < count; ++i) { + final DisplayContent dc = mChildren.get(i); + dc.updateInputWindows(inputMonitor, inputFocus, inDrag); + } + } + boolean reclaimSomeSurfaceMemory(WindowStateAnimator winAnimator, String operation, boolean secure) { final WindowSurfaceController surfaceController = winAnimator.mSurfaceController; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 772833c8ff4f8..b889db230208c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -589,6 +589,46 @@ class Task extends WindowContainer implements DimLayer.DimLayerU } } + void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token, + DisplayContent.GetWindowOnDisplaySearchResult result) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final AppWindowToken current = mChildren.get(i); + if (current == token) { + // We have reach the token we are interested in. End search. + result.reachedToken = true; + return; + } + + // We haven't reached the token yet; if this token is not going to the bottom and + // has windows on this display, then it is a candidate for what we are looking for. + final WindowList tokenWindowList = dc.getTokenWindowsOnDisplay(current); + if (!current.sendingToBottom && tokenWindowList.size() > 0) { + result.foundWindow = tokenWindowList.get(0); + } + } + } + + void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token, + DisplayContent.GetWindowOnDisplaySearchResult result) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final AppWindowToken current = mChildren.get(i); + if (!result.reachedToken) { + if (current == token) { + // We have reached the token we are interested in. Get whichever window occurs + // after it that is on the same display. + result.reachedToken = true; + } + continue; + } + + final WindowList tokenWindowList = dc.getTokenWindowsOnDisplay(current); + if (tokenWindowList.size() > 0) { + result.foundWindow = tokenWindowList.get(tokenWindowList.size() - 1); + return; + } + } + } + @Override boolean fillsParent() { return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index f2c74dffe4232..203ba72ca1444 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -859,9 +859,7 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye } // TODO: Should each user have there own stacks? - @Override void switchUser() { - super.switchUser(); int top = mChildren.size(); for (int taskNdx = 0; taskNdx < top; ++taskNdx) { Task task = mChildren.get(taskNdx); @@ -1490,6 +1488,30 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye } } + void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token, + DisplayContent.GetWindowOnDisplaySearchResult result) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final Task task = mChildren.get(i); + task.getWindowOnDisplayBeforeToken(dc, token, result); + if (result.reachedToken) { + // We have reach the token we are interested in. End search. + return; + } + } + } + + void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token, + DisplayContent.GetWindowOnDisplaySearchResult result) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final Task task = mChildren.get(i); + task.getWindowOnDisplayAfterToken(dc, token, result); + if (result.foundWindow != null) { + // We have found a window after the token. End search. + return; + } + } + } + @Override int getOrientation() { return (StackId.canSpecifyOrientation(mStackId)) diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7fd8028a9c137..8dbf2b3e019ff 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -683,6 +683,24 @@ class WallpaperController { } } + 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 { WindowState topWallpaper = null; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index f5db0b668c225..a6a907cd50fb0 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -18,6 +18,7 @@ package com.android.server.wm; import android.annotation.CallSuper; import android.content.res.Configuration; +import android.view.animation.Animation; import com.android.internal.util.ToBooleanFunction; import java.util.Comparator; @@ -482,11 +483,19 @@ class WindowContainer implements Comparable= 0; --i) { - mChildren.get(i).switchUser(); + /** + * Rebuilds the WindowList for the input display content. + * @param addIndex The index in the window list to add the next entry to. + * @return The next index in the window list to. + */ + // TODO: Hoping we can get rid of WindowList so this method wouldn't be needed. + int rebuildWindowList(int addIndex) { + final int count = mChildren.size(); + for (int i = 0; i < count; i++) { + final WindowContainer wc = mChildren.get(i); + addIndex = wc.rebuildWindowList(addIndex); } + return addIndex; } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 507679bedd4cf..00d8fba09a587 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -606,6 +606,17 @@ public class WindowManagerService extends IWindowManager.Stub boolean mInputMethodTargetWaitingAnim; WindowState mInputMethodWindow = null; + // TODO: Remove with extreme prejudice! This list is maintained so that we can keep track of + // dialogs that should go on top of the IME so they can be re-arranged in the window list any + // time the IME changes position in the window list. Normally you would have a dialog be a child + // window of the IME, but they don't share the same token since they are added by different + // clients. This doesn't really affect what the user sees on screen since this dialogs have an + // higher base layer than the IME window, but it will affect users of the window list that + // expect the list to represent the order of things on-screen (e.g input service). This makes + // the code for managing the window list hard to follow (see all the places it is used). + // We can remove the use of this field when we automatically assign layers and z-order the + // window list before it is used whenever window container order changes. + final ArrayList mInputMethodDialogs = new ArrayList<>(); boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; @@ -1366,16 +1377,19 @@ public class WindowManagerService extends IWindowManager.Stub boolean imMayMove = true; - win.mToken.addWindow(win); if (type == TYPE_INPUT_METHOD) { win.mGivenInsetsPending = true; mInputMethodWindow = win; - displayContent.computeImeTarget(true /* updateImeTarget */); + win.mToken.addImeWindow(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { - displayContent.computeImeTarget(true /* updateImeTarget */); + mInputMethodDialogs.add(win); + win.mToken.addWindow(win); + displayContent.moveInputMethodDialogs( + displayContent.findDesiredInputMethodWindowIndex(true)); imMayMove = false; } else { + win.mToken.addWindow(win); if (type == TYPE_WALLPAPER) { displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; @@ -1448,7 +1462,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (imMayMove) { - displayContent.computeImeTarget(true /* updateImeTarget */); + displayContent.moveInputMethodWindowsIfNeeded(false); } // Don't do layout here, the window must call @@ -1632,6 +1646,8 @@ public class WindowManagerService extends IWindowManager.Stub if (mInputMethodWindow == win) { mInputMethodWindow = null; + } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { + mInputMethodDialogs.remove(win); } final WindowToken token = win.mToken; @@ -1661,11 +1677,13 @@ public class WindowManagerService extends IWindowManager.Stub dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } - if (dc != null && !mWindowPlacerLocked.isInLayout()) { - dc.assignWindowLayers(true /* setLayoutNeeded */); - mWindowPlacerLocked.performSurfacePlacement(); - if (win.mAppToken != null) { - win.mAppToken.updateReportedVisibilityLocked(); + if (dc != null && dc.removeFromWindowList(win)) { + if (!mWindowPlacerLocked.isInLayout()) { + dc.assignWindowLayers(true /* setLayoutNeeded */); + mWindowPlacerLocked.performSurfacePlacement(); + if (win.mAppToken != null) { + win.mAppToken.updateReportedVisibilityLocked(); + } } } @@ -2046,15 +2064,14 @@ public class WindowManagerService extends IWindowManager.Stub // reassign them at this point if the IM window state gets shuffled boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; final DisplayContent dc = win.getDisplayContent(); - if (imMayMove) { - dc.computeImeTarget(true /* updateImeTarget */); - if (toBeDisplayed) { - // Little hack here -- we -should- be able to rely on the function to return - // true if the IME has moved and needs its layer recomputed. However, if the IME - // was hidden and isn't actually moved in the list, its layer may be out of data - // so we make sure to recompute it. - dc.assignWindowLayers(false /* setLayoutNeeded */); - } + if (imMayMove && (dc.moveInputMethodWindowsIfNeeded(false) || toBeDisplayed)) { + // Little hack here -- we -should- be able to rely on the function to return true if + // the IME has moved and needs its layer recomputed. However, if the IME was hidden + // and isn't actually moved in the list, its layer may be out of data so we make + // sure to recompute it. + // TODO: Probably not needed once the window list always has the right z-ordering + // when the window hierarchy is updated. + dc.assignWindowLayers(false /* setLayoutNeeded */); } if (wallpaperMayMove) { @@ -3334,7 +3351,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAppTransition.isTransitionSet()) { task.setSendingToBottom(false); } - displayContent.layoutAndAssignWindowLayersIfNeeded(); + displayContent.rebuildAppWindowsAndLayoutIfNeeded(); } } finally { Binder.restoreCallingIdentity(origId); @@ -3356,7 +3373,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAppTransition.isTransitionSet()) { task.setSendingToBottom(true); } - stack.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + stack.getDisplayContent().rebuildAppWindowsAndLayoutIfNeeded(); } } finally { Binder.restoreCallingIdentity(origId); @@ -4602,7 +4619,7 @@ public class WindowManagerService extends IWindowManager.Stub return null; } } - return displayContent.screenshotApplications(appToken, width, height, + return displayContent.screenshotApplications(appToken, displayId, width, height, includeFullDisplay, frameScale, config, wallpaperOnly); } @@ -5164,7 +5181,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean result = true; - final ArrayList windows = new ArrayList(); + final WindowList windows = new WindowList(); synchronized (mWindowMap) { mRoot.forAllWindows(w -> { windows.add(w); @@ -7295,7 +7312,7 @@ public class WindowManagerService extends IWindowManager.Stub changes |= FINISH_LAYOUT_REDO_LAYOUT; if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout"); - dc.computeImeTarget(true /* updateImeTarget */); + dc.moveInputMethodWindowsIfNeeded(true); mRoot.mWallpaperMayChange = true; // Since the window list has been rebuilt, focus might have to be recomputed since the // actual order of windows might have changed again. @@ -7388,25 +7405,10 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE); // TODO(multidisplay): Focused windows on default display only. final DisplayContent displayContent = getDefaultDisplayContentLocked(); - boolean imWindowChanged = false; - if (mInputMethodWindow != null) { - final WindowState prevTarget = mInputMethodTarget; - final WindowState newTarget = - displayContent.computeImeTarget(true /* updateImeTarget*/); - - imWindowChanged = prevTarget != newTarget; - - if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS - && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { - final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer; - displayContent.assignWindowLayers(false /* setLayoutNeeded */); - imWindowChanged |= - prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer; - } - } - + final boolean imWindowChanged = displayContent.moveInputMethodWindowsIfNeeded( + mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS + && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES); if (imWindowChanged) { - mWindowsChanged = true; displayContent.setLayoutNeeded(); newFocus = mRoot.computeFocusedWindow(); } @@ -7881,6 +7883,7 @@ public class WindowManagerService extends IWindowManager.Stub private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); mRoot.dumpTokens(pw, dumpAll); + mRoot.mWallpaperController.dumpTokens(pw, " ", dumpAll); if (!mFinishedStarting.isEmpty()) { pw.println(); pw.println(" Finishing start of application tokens:"); @@ -7926,6 +7929,16 @@ public class WindowManagerService extends IWindowManager.Stub ArrayList windows) { mRoot.dumpWindowsNoHeader(pw, dumpAll, windows); + if (mInputMethodDialogs.size() > 0) { + pw.println(); + pw.println(" Input method dialogs:"); + for (int i=mInputMethodDialogs.size()-1; i>=0; i--) { + WindowState w = mInputMethodDialogs.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" IM Dialog #"); pw.print(i); pw.print(": "); pw.println(w); + } + } + } if (mPendingRemove.size() > 0) { pw.println(); pw.println(" Remove pending for:"); @@ -8084,7 +8097,7 @@ public class WindowManagerService extends IWindowManager.Stub private boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti, boolean dumpAll) { - final ArrayList windows = new ArrayList(); + final WindowList windows = new WindowList(); if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) { final boolean appsOnly = name.contains("apps"); final boolean visibleOnly = name.contains("visible"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d959d8c715ca4..661df9cd60ada 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -76,7 +76,6 @@ import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_ import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; -import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; @@ -116,7 +115,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIO import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; @@ -142,6 +140,9 @@ import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; +class WindowList extends ArrayList { +} + /** A window in the window manager. */ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowState { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM; @@ -447,6 +448,12 @@ class WindowState extends WindowContainer implements WindowManagerP */ boolean mWindowRemovalAllowed; + /** + * Temp for keeping track of windows that have been removed when + * rebuilding window list. + */ + boolean mRebuilding; + // Input channel and input window handle used by the input dispatcher. final InputWindowHandle mInputWindowHandle; InputChannel mInputChannel; @@ -1714,7 +1721,7 @@ class WindowState extends WindowContainer implements WindowManagerP final DisplayContent dc = getDisplayContent(); if (mService.mInputMethodTarget == this) { - dc.computeImeTarget(true /* updateImeTarget */); + dc.moveInputMethodWindowsIfNeeded(false); } final int type = mAttrs.type; @@ -1924,33 +1931,6 @@ class WindowState extends WindowContainer implements WindowManagerP return mLayer + specialAdjustment; } - boolean canBeImeTarget() { - final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); - final int type = mAttrs.type; - - if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM) - && type != TYPE_APPLICATION_STARTING) { - return false; - } - - if (DEBUG_INPUT_METHOD) { - Slog.i(TAG_WM, "isVisibleOrAdding " + this + ": " + isVisibleOrAdding()); - if (!isVisibleOrAdding()) { - Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController - + " relayoutCalled=" + mRelayoutCalled - + " viewVis=" + mViewVisibility - + " policyVis=" + mPolicyVisibility - + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim - + " parentHidden=" + isParentWindowHidden() - + " exiting=" + mAnimatingExit + " destroying=" + mDestroying); - if (mAppToken != null) { - Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + mAppToken.hiddenRequested); - } - } - } - return isVisibleOrAdding(); - } - void scheduleAnimationIfDimming() { final DisplayContent dc = getDisplayContent(); if (dc == null) { @@ -2105,7 +2085,7 @@ class WindowState extends WindowContainer implements WindowManagerP return false; } - private void removeReplacedWindow() { + void removeReplacedWindow() { if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this); if (isDimming()) { transferDimToReplacement(); @@ -2158,16 +2138,6 @@ class WindowState extends WindowContainer implements WindowManagerP } } - @Override - void switchUser() { - super.switchUser(); - if (isHiddenFromUserLocked()) { - if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this - + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid); - hideLw(false); - } - } - int getTouchableRegion(Region region, int flags) { final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; if (modal && mAppToken != null) { @@ -3529,6 +3499,16 @@ class WindowState extends WindowContainer implements WindowManagerP return mIsChildWindow; } + /** + * Returns the bottom child window in regards to z-order of this window or null if no children. + */ + WindowState getBottomChild() { + // Child windows are z-ordered based on sub-layer using {@link #sWindowSubLayerComparator} + // and the child with the lowest z-order will be at the head of the list. + WindowState c = mChildren.peekFirst(); + return c == null ? null : c; + } + boolean layoutInParentFrame() { return mIsChildWindow && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0; @@ -3814,6 +3794,44 @@ class WindowState extends WindowContainer implements WindowManagerP return highestAnimLayer; } + @Override + int rebuildWindowList(int addIndex) { + return reAddWindow(addIndex); + } + + // TODO: come-up with a better name for this method that represents what it does. + // Or, it is probably not going to matter anyways if we are successful in getting rid of + // the WindowList concept. + int reAddWindow(int index) { + final DisplayContent dc = getDisplayContent(); + // Adding child windows relies on child windows being ordered by mSubLayer using + // {@link #sWindowSubLayerComparator}. + final int childCount = mChildren.size(); + boolean winAdded = false; + for (int j = 0; j < childCount; j++) { + final WindowState child = mChildren.get(j); + if (!winAdded && child.mSubLayer >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, + "Re-adding child window at " + index + ": " + child); + mRebuilding = false; + dc.addToWindowList(this, index); + index++; + winAdded = true; + } + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + child); + child.mRebuilding = false; + dc.addToWindowList(child, index); + index++; + } + if (!winAdded) { + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + this); + mRebuilding = false; + dc.addToWindowList(this, index); + index++; + } + return index; + } + @Override boolean forAllWindows(ToBooleanFunction callback, boolean traverseTopToBottom) { if (mChildren.isEmpty()) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index de808377e8808..7e1880fb65d6a 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -239,8 +239,7 @@ class WindowSurfacePlacer { mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); final DisplayContent displayContent = mService.getDefaultDisplayContentLocked(); - // TODO: Don't believe this is really needed... - //mService.mWindowsChanged = true; + displayContent.rebuildAppWindowList(); mService.mRoot.mWallpaperMayChange = false; @@ -357,8 +356,12 @@ class WindowSurfacePlacer { displayContent.setLayoutNeeded(); // TODO(multidisplay): IMEs are only supported on the default display. + // TODO: Probably not needed once the window list always has the right z-ordering + // when the window hierarchy is updated. final DisplayContent dc = mService.getDefaultDisplayContentLocked(); - dc.computeImeTarget(true /* updateImeTarget */); + if (!dc.moveInputMethodWindowsIfNeeded(true)) { + dc.assignWindowLayers(false /*setLayoutNeeded*/); + } mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); mService.mFocusMayChange = false; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 40bd3fb41b2fd..a2eebc336970a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -168,6 +168,13 @@ class WindowToken extends WindowContainer { return highestAnimLayer; } + WindowState getTopWindow() { + if (mChildren.isEmpty()) { + return null; + } + return (WindowState) mChildren.get(mChildren.size() - 1).getTop(); + } + /** * Returns true if the new window is considered greater than the existing window in terms of * z-order. @@ -182,16 +189,51 @@ class WindowToken extends WindowContainer { if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindow: win=" + win + " Callers=" + Debug.getCallers(5)); - if (win.isChildWindow()) { - // Child windows are added to their parent windows. + if (!win.isChildWindow()) { + if (asAppWindowToken() != null) { + mDisplayContent.addAppWindowToWindowList(win); + } else { + mDisplayContent.addNonAppWindowToWindowList(win); + } + + if (!mChildren.contains(win)) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this); + addChild(win, mWindowComparator); + } + } else { + mDisplayContent.addChildWindowToWindowList(win); + } + } + + void addImeWindow(WindowState win) { + int pos = mDisplayContent.findDesiredInputMethodWindowIndex(true); + + if (pos < 0) { + addWindow(win); + mDisplayContent.moveInputMethodDialogs(pos); return; } + + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, + "Adding input method window " + win + " at " + pos); + mDisplayContent.addToWindowList(win, pos); if (!mChildren.contains(win)) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this); - addChild(win, mWindowComparator); - mService.mWindowsChanged = true; - // TODO: Should we also be setting layout needed here and other places? + addChild(win, null); } + mDisplayContent.moveInputMethodDialogs(pos + 1); + } + + /** Return the first window in the token window list that isn't a starting window or null. */ + WindowState getFirstNonStartingWindow() { + final int count = mChildren.size(); + // We only care about parent windows so no need to loop through child windows. + for (int i = 0; i < count; i++) { + final WindowState w = mChildren.get(i); + if (w.mAttrs.type != TYPE_APPLICATION_STARTING) { + return w; + } + } + return null; } /** Returns true if the token windows list is empty. */ diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 3f47d5cfc2d36..50e5a2220ee3a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -116,6 +116,35 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(win2.hasChild(randomWindow)); } + @Test + public void testGetBottomChild() throws Exception { + final WindowState parentWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow"); + assertNull(parentWindow.getBottomChild()); + + final WindowState child1 = + createWindow(parentWindow, TYPE_APPLICATION_PANEL, mWindowToken, "child1"); + assertEquals(child1, parentWindow.getBottomChild()); + + final WindowState child2 = + createWindow(parentWindow, TYPE_APPLICATION_PANEL, mWindowToken, "child2"); + // Since child1 and child2 are at the same layer, then child2 is expect to be added on top + // on child1 + assertEquals(child1, parentWindow.getBottomChild()); + + final WindowState child3 = + createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY, mWindowToken, "child3"); + // Since child3 is a negative layer, we would expect it to be added below current children + // with positive layers. + assertEquals(child3, parentWindow.getBottomChild()); + + final WindowState child4 = + createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY, mWindowToken, "child4"); + // We would also expect additional negative layers to be added below existing negative + // layers. + assertEquals(child4, parentWindow.getBottomChild()); + } + @Test public void testGetParentWindow() throws Exception { final WindowState parentWindow =