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/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);
+ }
}