Made WindowState.mChildWindow private scoped
This involved:
- Moving method that operated on mChildWindows and mostly touched
WindowState fields into WindowState class.
- Creating new methods for getting information about child windows
outside WindowState class instead of accessing the child list directly.
- And, tests ;)
Bug: 30060889
Change-Id: I339cecb926b44b5db7ead1970e356304d5429c8f
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 894c27c..1d6cc32 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1206,33 +1206,10 @@
(int) windowFrame.right, (int) windowFrame.bottom);
}
- private static WindowInfo obtainPopulatedWindowInfo(WindowState windowState,
- Rect boundsInScreen) {
- WindowInfo window = WindowInfo.obtain();
- window.type = windowState.mAttrs.type;
- window.layer = windowState.mLayer;
- window.token = windowState.mClient.asBinder();
- window.title = windowState.mAttrs.accessibilityTitle;
- window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
-
- if (windowState.isChildWindow()) {
- window.parentToken = windowState.mParentWindow.mClient.asBinder();
- }
-
- window.focused = windowState.isFocused();
+ private static WindowInfo obtainPopulatedWindowInfo(
+ WindowState windowState, Rect boundsInScreen) {
+ final WindowInfo window = windowState.getWindowInfo();
window.boundsInScreen.set(boundsInScreen);
-
- final int childCount = windowState.mChildWindows.size();
- if (childCount > 0) {
- if (window.childTokens == null) {
- window.childTokens = new ArrayList<IBinder>();
- }
- for (int j = 0; j < childCount; j++) {
- WindowState child = windowState.mChildWindows.get(j);
- window.childTokens.add(child.mClient.asBinder());
- }
- }
-
return window;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2c0bb92..39a549d 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -424,7 +424,7 @@
final int numAllAppWinAnimators = mAllAppWinAnimators.size();
for (int i = 0; i < numAllAppWinAnimators; i++) {
- mAllAppWinAnimators.get(i).finishExit();
+ mAllAppWinAnimators.get(i).mWin.onExitAnimationDone();
}
mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
return false;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e464ffa..05ce7ae 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -362,7 +362,7 @@
win.destroyOrSaveSurface();
if (win.mRemoveOnExit) {
- service.removeWindowInnerLocked(win);
+ win.removeLocked();
}
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
@@ -680,10 +680,10 @@
candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false;
}
// Since the window already timed out, remove it immediately now.
- // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
+ // Use WindowState#removeLocked() instead of removeWindowLocked(), as the latter
// delays removal on certain conditions, which will leave the stale window in the
// stack and marked mWillReplaceWindow=false, so the window will never be removed.
- service.removeWindowInnerLocked(candidate);
+ candidate.removeLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 072e10f..7405cf9 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -119,18 +119,13 @@
mInputMethodAnimLayerAdjustment = adj;
final WindowState imw = mService.mInputMethodWindow;
if (imw != null) {
- imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
- + " anim layer: " + imw.mWinAnimator.mAnimLayer);
- for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
- final WindowState childWindow = imw.mChildWindows.get(i);
- childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
- + " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
- }
+ 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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9c7e0f8..76189be 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1146,14 +1146,9 @@
private int indexOfWinInWindowList(WindowState targetWin, WindowList windows) {
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState w = windows.get(i);
- if (w == targetWin) {
+ if (w == targetWin || w.hasChild(targetWin)) {
return i;
}
- if (!w.mChildWindows.isEmpty()) {
- if (indexOfWinInWindowList(targetWin, w.mChildWindows) >= 0) {
- return i;
- }
- }
}
return -1;
}
@@ -1646,30 +1641,6 @@
moveInputMethodDialogsLocked(pos);
}
- private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
- WindowList windows = win.getWindowList();
- int wpos = windows.indexOf(win);
- if (wpos >= 0) {
- if (wpos < interestingPos) interestingPos--;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + win);
- windows.remove(wpos);
- mWindowsChanged = true;
- int NC = win.mChildWindows.size();
- while (NC > 0) {
- NC--;
- WindowState cw = win.mChildWindows.get(NC);
- int cpos = windows.indexOf(cw);
- if (cpos >= 0) {
- if (cpos < interestingPos) interestingPos--;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing child at "
- + cpos + ": " + cw);
- windows.remove(cpos);
- }
- }
- }
- return interestingPos;
- }
-
private void reAddWindowToListInOrderLocked(WindowState win) {
addWindowToListInOrderLocked(win, false);
// This is a hack to get all of the child windows added as well
@@ -1681,7 +1652,7 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win);
windows.remove(wpos);
mWindowsChanged = true;
- reAddWindowLocked(wpos, win);
+ win.reAddWindowLocked(wpos);
}
}
@@ -1701,7 +1672,7 @@
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 = tmpRemoveWindowLocked(pos, dialogs.get(i));
+ pos = dialogs.get(i).removeFromWindowList(pos);
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "Window list w/pos=" + pos);
@@ -1725,7 +1696,7 @@
for (int i=0; i<N; i++) {
WindowState win = dialogs.get(i);
win.mTargetAppToken = targetAppToken;
- pos = reAddWindowLocked(pos, win);
+ pos = win.reAddWindowLocked(pos);
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "Final window list:");
@@ -1762,16 +1733,14 @@
// First check to see if the input method windows are already
// located here, and contiguous.
final int N = windows.size();
- WindowState firstImWin = imPos < N
- ? windows.get(imPos) : null;
+ final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
// Figure out the actual input method window that should be
// at the bottom of their stack.
- WindowState baseImWin = imWin != null
- ? imWin : mInputMethodDialogs.get(0);
- if (baseImWin.mChildWindows.size() > 0) {
- WindowState cw = baseImWin.mChildWindows.get(0);
- if (cw.mSubLayer < 0) baseImWin = cw;
+ WindowState baseImWin = imWin != null ? imWin : mInputMethodDialogs.get(0);
+ final WindowState cw = baseImWin.getBottomChild();
+ if (cw != null && cw.mSubLayer < 0) {
+ baseImWin = cw;
}
if (firstImWin == baseImWin) {
@@ -1807,13 +1776,13 @@
Slog.v(TAG_WM, "Moving IM from " + imPos);
logWindowList(windows, " ");
}
- imPos = tmpRemoveWindowLocked(imPos, imWin);
+ imPos = imWin.removeFromWindowList(imPos);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
logWindowList(windows, " ");
}
imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
- reAddWindowLocked(imPos, imWin);
+ imWin.reAddWindowLocked(imPos);
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
logWindowList(windows, " ");
@@ -1829,7 +1798,7 @@
if (imWin != null) {
if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos);
- tmpRemoveWindowLocked(0, imWin);
+ imWin.removeFromWindowList(0);
imWin.mTargetAppToken = null;
reAddWindowToListInOrderLocked(imWin);
if (DEBUG_INPUT_METHOD) {
@@ -1850,7 +1819,7 @@
return true;
}
- private static boolean excludeWindowTypeFromTapOutTask(int windowType) {
+ static boolean excludeWindowTypeFromTapOutTask(int windowType) {
switch (windowType) {
case TYPE_STATUS_BAR:
case TYPE_NAVIGATION_BAR:
@@ -2454,7 +2423,7 @@
}
}
- removeWindowInnerLocked(win);
+ win.removeLocked();
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
@@ -2464,41 +2433,12 @@
Binder.restoreCallingIdentity(origId);
}
- void removeWindowInnerLocked(WindowState win) {
- if (win.mRemoved) {
- // Nothing to do.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "removeWindowInnerLocked: " + win + " Already removed...");
- return;
- }
-
- for (int i = win.mChildWindows.size() - 1; i >= 0; i--) {
- WindowState cwin = win.mChildWindows.get(i);
- Slog.w(TAG_WM, "Force-removing child win " + cwin + " from container " + win);
- removeWindowInnerLocked(cwin);
- }
-
- win.mRemoved = true;
-
- if (mInputMethodTarget == win) {
- moveInputMethodWindowsIfNeededLocked(false);
- }
-
- if (false) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.w(TAG_WM, "Removing window " + win, e);
- }
-
- final int type = win.mAttrs.type;
- if (excludeWindowTypeFromTapOutTask(type)) {
- final DisplayContent displaycontent = win.getDisplayContent();
- displaycontent.mTapExcludedWindows.remove(win);
- }
- mPolicy.removeWindowLw(win);
- win.removeLocked();
-
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "removeWindowInnerLocked: " + win);
+ /**
+ * Performs some centralized bookkeeping clean-up on the window that is being removed.
+ * NOTE: Should only be called from {@link WindowState#removeLocked()}
+ */
+ void postWindowRemoveCleanupLocked(WindowState win) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
if (win.mAppOp != AppOpsManager.OP_NONE) {
mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
@@ -2551,7 +2491,7 @@
}
}
- if (type == TYPE_WALLPAPER) {
+ if (win.mAttrs.type == TYPE_WALLPAPER) {
mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -4377,7 +4317,7 @@
WindowState win = wtoken.allAppWindows.get(i);
if (win == wtoken.startingWindow) {
// Starting window that's exiting will be removed when the animation
- // finishes. Mark all relevant flags for that finishExit will proceed
+ // finishes. Mark all relevant flags for that onExitAnimationDone will proceed
// all the way to actually remove it.
if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) {
win.mAnimatingExit = true;
@@ -4865,38 +4805,6 @@
}
}
- private final int reAddWindowLocked(int index, WindowState win) {
- final WindowList windows = win.getWindowList();
- // Adding child windows relies on mChildWindows being ordered by mSubLayer.
- final int NCW = win.mChildWindows.size();
- boolean winAdded = false;
- for (int j=0; j<NCW; j++) {
- WindowState cwin = win.mChildWindows.get(j);
- if (!winAdded && cwin.mSubLayer >= 0) {
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding child window at "
- + index + ": " + cwin);
- win.mRebuilding = false;
- windows.add(index, win);
- index++;
- winAdded = true;
- }
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
- + index + ": " + cwin);
- cwin.mRebuilding = false;
- windows.add(index, cwin);
- index++;
- }
- if (!winAdded) {
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
- + index + ": " + win);
- win.mRebuilding = false;
- windows.add(index, win);
- index++;
- }
- mWindowsChanged = true;
- return index;
- }
-
private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index,
WindowToken token) {
final int NW = token.windows.size();
@@ -4905,7 +4813,7 @@
final DisplayContent winDisplayContent = win.getDisplayContent();
if (winDisplayContent == displayContent || winDisplayContent == null) {
win.mDisplayContent = displayContent;
- index = reAddWindowLocked(index, win);
+ index = win.reAddWindowLocked(index);
}
}
return index;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5c40947..4dbac7e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -49,6 +49,7 @@
import android.view.InputEventReceiver;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
@@ -61,6 +62,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -100,6 +102,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+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;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -107,6 +110,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
@@ -157,7 +161,7 @@
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mParentWindow;
- final WindowList mChildWindows = new WindowList();
+ private final WindowList mChildWindows = new WindowList();
final int mBaseLayer;
final int mSubLayer;
final boolean mLayoutAttached;
@@ -1454,6 +1458,31 @@
}
void removeLocked() {
+ if (mRemoved) {
+ // Nothing to do.
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "WS.removeLocked: " + this + " Already removed...");
+ return;
+ }
+
+ for (int i = mChildWindows.size() - 1; i >= 0; i--) {
+ final WindowState child = mChildWindows.get(i);
+ Slog.w(TAG_WM, "Force-removing child win " + child + " from container " + this);
+ child.removeLocked();
+ }
+
+ mRemoved = true;
+
+ if (mService.mInputMethodTarget == this) {
+ mService.moveInputMethodWindowsIfNeededLocked(false);
+ }
+
+ final int type = mAttrs.type;
+ if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
+ final DisplayContent displaycontent = getDisplayContent();
+ displaycontent.mTapExcludedWindows.remove(this);
+ }
+ mPolicy.removeWindowLw(this);
+
disposeInputChannel();
if (isChildWindow()) {
@@ -1469,6 +1498,8 @@
// Ignore if it has already been removed (usually because
// we are doing this as part of processing a death note.)
}
+
+ mService.postWindowRemoveCleanupLocked(this);
}
void setHasSurface(boolean hasSurface) {
@@ -1630,7 +1661,7 @@
win.mReplacingWindow = null;
mSkipEnterAnimationForSeamlessReplacement = false;
if (win.mAnimatingExit || !animateReplacingWindow) {
- mService.removeWindowInnerLocked(win);
+ win.removeLocked();
}
}
}
@@ -2830,6 +2861,24 @@
return mParentWindow != null;
}
+ boolean hasChild(WindowState child) {
+ for (int i = mChildWindows.size() - 1; i >= 0; --i) {
+ if (mChildWindows.get(i) == child) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ return mChildWindows.isEmpty() ? null : mChildWindows.get(0);
+ }
+
boolean layoutInParentFrame() {
return isChildWindow() && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0;
}
@@ -2983,4 +3032,170 @@
+ " Callers=" + Debug.getCallers(4));
}
}
+
+ WindowInfo getWindowInfo() {
+ WindowInfo windowInfo = WindowInfo.obtain();
+ windowInfo.type = mAttrs.type;
+ windowInfo.layer = mLayer;
+ windowInfo.token = mClient.asBinder();
+ windowInfo.title = mAttrs.accessibilityTitle;
+ windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
+ windowInfo.focused = isFocused();
+
+ if (isChildWindow()) {
+ windowInfo.parentToken = mParentWindow.mClient.asBinder();
+ }
+
+ final int childCount = mChildWindows.size();
+ if (childCount > 0) {
+ if (windowInfo.childTokens == null) {
+ windowInfo.childTokens = new ArrayList<>();
+ }
+ for (int j = 0; j < childCount; j++) {
+ final WindowState child = mChildWindows.get(j);
+ windowInfo.childTokens.add(child.mClient.asBinder());
+ }
+ }
+
+ return windowInfo;
+ }
+
+ void adjustAnimLayer(int adj) {
+ mWinAnimator.mAnimLayer = mLayer + adj;
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "win=" + this + " anim layer: " + mWinAnimator.mAnimLayer);
+ for (int i = mChildWindows.size() - 1; i >= 0; i--) {
+ final WindowState child = mChildWindows.get(i);
+ child.adjustAnimLayer(adj);
+ }
+ }
+
+ // 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 reAddWindowLocked(int index) {
+ final WindowList windows = getWindowList();
+ // Adding child windows relies on child windows being ordered by mSubLayer using
+ // {@link #sWindowSubLayerComparator}.
+ final int childCount = mChildWindows.size();
+ boolean winAdded = false;
+ for (int j = 0; j < childCount; j++) {
+ WindowState child = mChildWindows.get(j);
+ if (!winAdded && child.mSubLayer >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
+ "Re-adding child window at " + index + ": " + child);
+ mRebuilding = false;
+ windows.add(index, this);
+ index++;
+ winAdded = true;
+ }
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + child);
+ child.mRebuilding = false;
+ windows.add(index, child);
+ index++;
+ }
+ if (!winAdded) {
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + this);
+ mRebuilding = false;
+ windows.add(index, this);
+ index++;
+ }
+ mService.mWindowsChanged = true;
+ return index;
+ }
+
+ int removeFromWindowList(int interestingPos) {
+ final WindowList windows = getWindowList();
+ int wpos = windows.indexOf(this);
+ if (wpos < 0) {
+ return interestingPos;
+ }
+
+ if (wpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
+ windows.remove(wpos);
+ mService.mWindowsChanged = true;
+ int childCount = mChildWindows.size();
+ while (childCount > 0) {
+ childCount--;
+ final WindowState cw = mChildWindows.get(childCount);
+ int cpos = windows.indexOf(cw);
+ if (cpos >= 0) {
+ if (cpos < interestingPos) interestingPos--;
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
+ "Temp removing child at " + cpos + ": " + cw);
+ windows.remove(cpos);
+ }
+ }
+ return interestingPos;
+ }
+
+ void onExitAnimationDone() {
+ if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
+ + " windowAnimating=" + mWinAnimator.isWindowAnimationSet());
+
+ if (!mChildWindows.isEmpty()) {
+ // Copying to a different list as multiple children can be removed.
+ final WindowList childWindows = new WindowList(mChildWindows);
+ for (int i = childWindows.size() - 1; i >= 0; i--) {
+ childWindows.get(i).onExitAnimationDone();
+ }
+ }
+
+ if (mWinAnimator.mEnteringAnimation) {
+ mWinAnimator.mEnteringAnimation = false;
+ mService.requestTraversal();
+ // System windows don't have an activity and an app token as a result, but need a way
+ // to be informed about their entrance animation end.
+ if (mAppToken == null) {
+ try {
+ mClient.dispatchWindowShown();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ if (!mWinAnimator.isWindowAnimationSet()) {
+ //TODO (multidisplay): Accessibility is supported only for the default display.
+ if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+ }
+
+ if (!mAnimatingExit) {
+ return;
+ }
+
+ if (mWinAnimator.isWindowAnimationSet()) {
+ return;
+ }
+
+ if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
+
+ mDestroying = true;
+
+ final boolean hasSurface = mWinAnimator.hasSurface();
+ if (hasSurface) {
+ mWinAnimator.hide("onExitAnimationDone");
+ }
+
+ // If we have an app token, we ask it to destroy the surface for us, so that it can take
+ // care to ensure the activity has actually stopped and the surface is not still in use.
+ // Otherwise we add the service to mDestroySurface and allow it to be processed in our next
+ // transaction.
+ if (mAppToken != null) {
+ mAppToken.destroySurfaces();
+ } else {
+ if (hasSurface) {
+ mService.mDestroySurface.add(this);
+ }
+ if (mRemoveOnExit) {
+ mService.mPendingRemove.add(this);
+ mRemoveOnExit = false;
+ }
+ }
+ mAnimatingExit = false;
+ mService.mWallpaperControllerLocked.hideWallpapers(this);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9d3a221..673d28c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -24,7 +24,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
@@ -53,7 +52,6 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
-import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
import android.view.DisplayInfo;
@@ -102,7 +100,7 @@
// Unchanging local convenience fields.
final WindowManagerService mService;
final WindowState mWin;
- final WindowStateAnimator mAttachedWinAnimator;
+ private final WindowStateAnimator mParentWinAnimator;
final WindowAnimator mAnimator;
AppWindowAnimator mAppAnimator;
final Session mSession;
@@ -260,8 +258,7 @@
}
mWin = win;
- mAttachedWinAnimator = !win.isChildWindow()
- ? null : win.mParentWindow.mWinAnimator;
+ mParentWinAnimator = !win.isChildWindow() ? null : win.mParentWindow.mWinAnimator;
mAppAnimator = win.mAppToken == null ? null : win.mAppToken.mAppAnimator;
mSession = win.mSession;
mAttrType = win.mAttrs.type;
@@ -309,7 +306,7 @@
*/
boolean isAnimationSet() {
return mAnimation != null
- || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null)
+ || (mParentWinAnimator != null && mParentWinAnimator.mAnimation != null)
|| (mAppAnimator != null && mAppAnimator.isAnimating());
}
@@ -490,7 +487,7 @@
}
}
- finishExit();
+ mWin.onExitAnimationDone();
final int displayId = mWin.getDisplayId();
mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (DEBUG_LAYOUT_REPEATS)
@@ -504,80 +501,6 @@
return false;
}
- void finishExit() {
- if (DEBUG_ANIM) Slog.v(
- TAG, "finishExit in " + this
- + ": exiting=" + mWin.mAnimatingExit
- + " remove=" + mWin.mRemoveOnExit
- + " windowAnimating=" + isWindowAnimationSet());
-
- if (!mWin.mChildWindows.isEmpty()) {
- // Copying to a different list as multiple children can be removed.
- final WindowList childWindows = new WindowList(mWin.mChildWindows);
- for (int i = childWindows.size() - 1; i >= 0; i--) {
- childWindows.get(i).mWinAnimator.finishExit();
- }
- }
-
- if (mEnteringAnimation) {
- mEnteringAnimation = false;
- mService.requestTraversal();
- // System windows don't have an activity and an app token as a result, but need a way
- // to be informed about their entrance animation end.
- if (mWin.mAppToken == null) {
- try {
- mWin.mClient.dispatchWindowShown();
- } catch (RemoteException e) {
- }
- }
- }
-
- if (!isWindowAnimationSet()) {
- //TODO (multidisplay): Accessibility is supported only for the default display.
- if (mService.mAccessibilityController != null
- && mWin.getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
- }
- }
-
- if (!mWin.mAnimatingExit) {
- return;
- }
-
- if (isWindowAnimationSet()) {
- return;
- }
-
- if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
- "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit);
-
-
- mWin.mDestroying = true;
-
- final boolean hasSurface = hasSurface();
- if (hasSurface) {
- hide("finishExit");
- }
-
- // If we have an app token, we ask it to destroy the surface for us,
- // so that it can take care to ensure the activity has actually stopped
- // and the surface is not still in use. Otherwise we add the service to
- // mDestroySurface and allow it to be processed in our next transaction.
- if (mWin.mAppToken != null) {
- mWin.mAppToken.destroySurfaces();
- } else {
- if (hasSurface) {
- mService.mDestroySurface.add(mWin);
- }
- if (mWin.mRemoveOnExit) {
- mService.mPendingRemove.add(mWin);
- mWin.mRemoveOnExit = false;
- }
- }
- mWin.mAnimatingExit = false;
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
-
void hide(String reason) {
if (!mLastHidden) {
//dump();
@@ -925,8 +848,8 @@
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
- (mAttachedWinAnimator != null && mAttachedWinAnimator.mHasLocalTransformation)
- ? mAttachedWinAnimator.mTransformation : null;
+ (mParentWinAnimator != null && mParentWinAnimator.mHasLocalTransformation)
+ ? mParentWinAnimator.mTransformation : null;
Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
? mAppAnimator.transformation : null;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7cf08d2..1b9eeb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -213,9 +213,9 @@
recoveringMemory = true;
// Wait a little bit for things to settle down, and off we go.
while (!mService.mForceRemoves.isEmpty()) {
- WindowState ws = mService.mForceRemoves.remove(0);
+ final WindowState ws = mService.mForceRemoves.remove(0);
Slog.i(TAG, "Force removing: " + ws);
- mService.removeWindowInnerLocked(ws);
+ ws.removeLocked();
}
Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
Object tmp = new Object();
@@ -544,8 +544,8 @@
mService.mPendingRemove.clear();
DisplayContentList displayList = new DisplayContentList();
for (i = 0; i < N; i++) {
- WindowState w = mService.mPendingRemoveTmp[i];
- mService.removeWindowInnerLocked(w);
+ final WindowState w = mService.mPendingRemoveTmp[i];
+ w.removeLocked();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
displayList.add(displayContent);
@@ -1269,7 +1269,7 @@
// We also don't clear the mAnimatingExit flag for windows which have the
// mRemoveOnExit flag. This indicates an explicit remove request has been issued
// by the client. We should let animation proceed and not clear this flag or
- // they won't eventually be removed by WindowStateAnimator#finishExit.
+ // they won't eventually be removed by WindowState#onExitAnimationDone.
if (!win.mWillReplaceWindow && !win.mRemoveOnExit) {
win.mAnimatingExit = false;
// Clear mAnimating flag together with mAnimatingExit. When animation
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3bb797e..936949e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,17 +19,16 @@
import com.android.internal.policy.IShortcutService;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
@@ -37,7 +36,51 @@
import java.io.PrintWriter;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
public class TestWindowManagerPolicy implements WindowManagerPolicy {
+ private static final String TAG = "TestWindowManagerPolicy";
@Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
@@ -89,11 +132,124 @@
@Override
public int windowTypeToLayerLw(int type) {
- return 0;
+ // TODO: figure-out a good way to keep this in-sync with PhoneWindowManager...sigh!
+ if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
+ return 2;
+ }
+ switch (type) {
+ case TYPE_PRIVATE_PRESENTATION:
+ return 2;
+ case TYPE_WALLPAPER:
+ // wallpaper is at the bottom, though the window manager may move it.
+ return 2;
+ case TYPE_DOCK_DIVIDER:
+ return 2;
+ case TYPE_QS_DIALOG:
+ return 2;
+ case TYPE_PHONE:
+ return 3;
+ case TYPE_SEARCH_BAR:
+ case TYPE_VOICE_INTERACTION_STARTING:
+ return 4;
+ case TYPE_VOICE_INTERACTION:
+ // voice interaction layer is almost immediately above apps.
+ return 5;
+ case TYPE_INPUT_CONSUMER:
+ return 6;
+ case TYPE_SYSTEM_DIALOG:
+ return 7;
+ case TYPE_TOAST:
+ // toasts and the plugged-in battery thing
+ return 8;
+ case TYPE_PRIORITY_PHONE:
+ // SIM errors and unlock. Not sure if this really should be in a high layer.
+ return 9;
+ case TYPE_DREAM:
+ // used for Dreams (screensavers with TYPE_DREAM windows)
+ return 10;
+ case TYPE_SYSTEM_ALERT:
+ // like the ANR / app crashed dialogs
+ return 11;
+ case TYPE_INPUT_METHOD:
+ // on-screen keyboards and other such input method user interfaces go here.
+ return 12;
+ case TYPE_INPUT_METHOD_DIALOG:
+ // on-screen keyboards and other such input method user interfaces go here.
+ return 13;
+ case TYPE_KEYGUARD_SCRIM:
+ // the safety window that shows behind keyguard while keyguard is starting
+ return 14;
+ case TYPE_STATUS_BAR_SUB_PANEL:
+ return 15;
+ case TYPE_STATUS_BAR:
+ return 16;
+ case TYPE_STATUS_BAR_PANEL:
+ return 17;
+ case TYPE_KEYGUARD_DIALOG:
+ return 18;
+ case TYPE_VOLUME_OVERLAY:
+ // the on-screen volume indicator and controller shown when the user
+ // changes the device volume
+ return 19;
+ case TYPE_SYSTEM_OVERLAY:
+ // the on-screen volume indicator and controller shown when the user
+ // changes the device volume
+ return 20;
+ case TYPE_NAVIGATION_BAR:
+ // the navigation bar, if available, shows atop most things
+ return 21;
+ case TYPE_NAVIGATION_BAR_PANEL:
+ // some panels (e.g. search) need to show on top of the navigation bar
+ return 22;
+ case TYPE_SCREENSHOT:
+ // screenshot selection layer shouldn't go above system error, but it should cover
+ // navigation bars at the very least.
+ return 23;
+ case TYPE_SYSTEM_ERROR:
+ // system-level error dialogs
+ return 24;
+ case TYPE_MAGNIFICATION_OVERLAY:
+ // used to highlight the magnified portion of a display
+ return 25;
+ case TYPE_DISPLAY_OVERLAY:
+ // used to simulate secondary display devices
+ return 26;
+ case TYPE_DRAG:
+ // the drag layer: input for drag-and-drop is associated with this window,
+ // which sits above all other focusable windows
+ return 27;
+ case TYPE_ACCESSIBILITY_OVERLAY:
+ // overlay put by accessibility services to intercept user interaction
+ return 28;
+ case TYPE_SECURE_SYSTEM_OVERLAY:
+ return 29;
+ case TYPE_BOOT_PROGRESS:
+ return 30;
+ case TYPE_POINTER:
+ // the (mouse) pointer layer
+ return 31;
+ }
+ Log.e(TAG, "Unknown window type: " + type);
+ return 2;
}
@Override
public int subWindowTypeToLayerLw(int type) {
+ // TODO: figure-out a good way to keep this in-sync with PhoneWindowManager...
+ switch (type) {
+ case TYPE_APPLICATION_PANEL:
+ case TYPE_APPLICATION_ATTACHED_DIALOG:
+ return 1;
+ case TYPE_APPLICATION_MEDIA:
+ return -2;
+ case TYPE_APPLICATION_MEDIA_OVERLAY:
+ return -1;
+ case TYPE_APPLICATION_SUB_PANEL:
+ return 2;
+ case TYPE_APPLICATION_ABOVE_SUB_PANEL:
+ return 3;
+ }
+ Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
}
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 b66a72a..68c64f7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -16,14 +16,20 @@
package com.android.server.wm;
+import com.android.server.LocalServices;
+
import android.content.Context;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.IWindow;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
/**
* Tests for the {@link WindowState} class.
@@ -32,9 +38,10 @@
* Install: adb install -r out/target/product/angler/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
* Run: adb shell am instrument -w -e class com.android.server.wm.WindowStateTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
*/
+@SmallTest
public class WindowStateTests extends AndroidTestCase {
- private WindowManagerService mWm;
+ private static WindowManagerService sWm = null;
private WindowToken mWindowToken;
private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
private final IWindow mIWindow = new TestIWindow();
@@ -43,14 +50,18 @@
public void setUp() throws Exception {
final Context context = getContext();
// final InputManagerService im = new InputManagerService(context);
- mWm = WindowManagerService.main(context, /*im*/ null, true, false, false, mPolicy);
- mWindowToken = new WindowToken(mWm, null, 0, false);
+ if (sWm == null) {
+ // We only want to do this once for the test process as we don't want WM to try to
+ // register a bunch of local services again.
+ sWm = WindowManagerService.main(context, /*im*/ null, true, false, false, mPolicy);
+ }
+ mWindowToken = new WindowToken(sWm, null, 0, false);
}
public void testIsParentWindowHidden() throws Exception {
- final WindowState parentWindow = createWindow(null);
- final WindowState child1 = createWindow(parentWindow);
- final WindowState child2 = createWindow(parentWindow);
+ final WindowState parentWindow = createWindow(null, TYPE_APPLICATION);
+ final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW);
+ final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW);
assertFalse(parentWindow.mHidden);
assertFalse(parentWindow.isParentWindowHidden());
@@ -63,11 +74,65 @@
assertTrue(child2.isParentWindowHidden());
}
- private WindowState createWindow(WindowState parent) {
- final int type = (parent == null) ? TYPE_APPLICATION : FIRST_SUB_WINDOW;
+ public void testIsChildWindow() throws Exception {
+ final WindowState parentWindow = createWindow(null, TYPE_APPLICATION);
+ final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW);
+ final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW);
+ final WindowState randomWindow = createWindow(null, TYPE_APPLICATION);
+
+ assertFalse(parentWindow.isChildWindow());
+ assertTrue(child1.isChildWindow());
+ assertTrue(child2.isChildWindow());
+ assertFalse(randomWindow.isChildWindow());
+ }
+
+ public void testHasChild() throws Exception {
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION);
+ final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW);
+ final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW);
+ final WindowState win2 = createWindow(null, TYPE_APPLICATION);
+ final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW);
+ final WindowState randomWindow = createWindow(null, TYPE_APPLICATION);
+
+ assertTrue(win1.hasChild(win11));
+ assertTrue(win1.hasChild(win12));
+ assertTrue(win2.hasChild(win21));
+
+ assertFalse(win1.hasChild(win21));
+ assertFalse(win1.hasChild(randomWindow));
+
+ assertFalse(win2.hasChild(win11));
+ assertFalse(win2.hasChild(win12));
+ assertFalse(win2.hasChild(randomWindow));
+ }
+
+ public void testGetBottomChild() throws Exception {
+ final WindowState parentWindow = createWindow(null, TYPE_APPLICATION);
+ assertNull(parentWindow.getBottomChild());
+
+ final WindowState child1 = createWindow(parentWindow, TYPE_APPLICATION_PANEL);
+ assertEquals(child1, parentWindow.getBottomChild());
+
+ final WindowState child2 = createWindow(parentWindow, TYPE_APPLICATION_PANEL);
+ // 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);
+ // 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);
+ // We would also expect additional negative layers to be added below existing negative
+ // layers.
+ assertEquals(child4, parentWindow.getBottomChild());
+ }
+
+ private WindowState createWindow(WindowState parent, int type) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
- return new WindowState(mWm, null, mIWindow, mWindowToken, parent, 0, 0, attrs, 0,
- mWm.getDefaultDisplayContentLocked(), 0);
+ return new WindowState(sWm, null, mIWindow, mWindowToken, parent, 0, 0, attrs, 0,
+ sWm.getDefaultDisplayContentLocked(), 0);
}
}