Fixed issues with child windows been IME targets
- Fixed issue with WindowState.getWindow() returning the parent window before
its children with positive sub-layers. Positive sub-layer children should be
returned first, then the parent window, and then negative sub-layer children.
This was causing the the parent window to be selected as the IME target
instead of the child on-top of it since they both can be IME targets and the
parent window was returned first.
- Fixed issue with WindowState.forAllWindow() not returning the IME window if
the current IME target is a child window.
- Add test WindowStateTest.testGetWindow(),
DisplayContentTests.testForAllWindows_WithChildWindowImeTarget(), and
DisplayContentTests.testComputeImeTarget() to cover the failing cases.
Change-Id: I0c93e0344601fc870011e8a8d84528f62b0a2a06
Fixes: 34786357
Fixes: 34306127
Fixes: 34711958
Fixes: 35362942
Test: bit FrameworksServicesTests:com.android.server.wm.WindowStateTests
Test: bit FrameworksServicesTests:com.android.server.wm.DisplayContentTests
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 65e1f84..4cd1af9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3876,7 +3876,7 @@
}
private boolean forAllWindowBottomToTop(ToBooleanFunction<WindowState> callback) {
- // We want to consumer the negative sublayer children first because they need to appear
+ // We want to consume the negative sublayer children first because they need to appear
// below the parent, then this window (the parent), and then the positive sublayer children
// because they need to appear above the parent.
int i = 0;
@@ -3884,7 +3884,7 @@
WindowState child = mChildren.get(i);
while (i < count && child.mSubLayer < 0) {
- if (callback.apply(child)) {
+ if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
return true;
}
i++;
@@ -3899,7 +3899,7 @@
}
while (i < count) {
- if (callback.apply(child)) {
+ if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
return true;
}
i++;
@@ -3913,14 +3913,14 @@
}
private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
- // We want to consumer the positive sublayer children first because they need to appear
+ // We want to consume the positive sublayer children first because they need to appear
// above the parent, then this window (the parent), and then the negative sublayer children
// because they need to appear above the parent.
int i = mChildren.size() - 1;
WindowState child = mChildren.get(i);
while (i >= 0 && child.mSubLayer >= 0) {
- if (callback.apply(child)) {
+ if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
return true;
}
--i;
@@ -3935,7 +3935,7 @@
}
while (i >= 0) {
- if (callback.apply(child)) {
+ if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
return true;
}
--i;
@@ -3978,10 +3978,43 @@
}
WindowState getWindow(Predicate<WindowState> callback) {
+ if (mChildren.isEmpty()) {
+ return callback.test(this) ? this : null;
+ }
+
+ // We want to consume the positive sublayer children first because they need to appear
+ // above the parent, then this window (the parent), and then the negative sublayer children
+ // because they need to appear above the parent.
+ int i = mChildren.size() - 1;
+ WindowState child = mChildren.get(i);
+
+ while (i >= 0 && child.mSubLayer >= 0) {
+ if (callback.test(child)) {
+ return child;
+ }
+ --i;
+ if (i < 0) {
+ break;
+ }
+ child = mChildren.get(i);
+ }
+
if (callback.test(this)) {
return this;
}
- return super.getWindow(callback);
+
+ while (i >= 0) {
+ if (callback.test(child)) {
+ return child;
+ }
+ --i;
+ if (i < 0) {
+ break;
+ }
+ child = mChildren.get(i);
+ }
+
+ return null;
}
boolean isWindowAnimationSet() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 30f99e5..bd3271b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,24 +16,25 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
+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_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-import java.util.ArrayList;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static org.junit.Assert.assertEquals;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
/**
* Tests for the {@link DisplayContent} class.
@@ -54,38 +55,17 @@
exitingAppToken.mIsExiting = true;
exitingAppToken.mTask.mStack.mExitingAppTokens.add(exitingAppToken);
- final ArrayList<WindowState> windows = new ArrayList();
-
- // Test forward traversal.
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(0));
- assertEquals(exitingAppWindow, windows.get(1));
- assertEquals(sChildAppWindowBelow, windows.get(2));
- assertEquals(sAppWindow, windows.get(3));
- assertEquals(sChildAppWindowAbove, windows.get(4));
- assertEquals(sDockedDividerWindow, windows.get(5));
- assertEquals(sStatusBarWindow, windows.get(6));
- assertEquals(sNavBarWindow, windows.get(7));
- assertEquals(sImeWindow, windows.get(8));
- assertEquals(sImeDialogWindow, windows.get(9));
-
- // Test backward traversal.
- windows.clear();
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(9));
- assertEquals(exitingAppWindow, windows.get(8));
- assertEquals(sChildAppWindowBelow, windows.get(7));
- assertEquals(sAppWindow, windows.get(6));
- assertEquals(sChildAppWindowAbove, windows.get(5));
- assertEquals(sDockedDividerWindow, windows.get(4));
- assertEquals(sStatusBarWindow, windows.get(3));
- assertEquals(sNavBarWindow, windows.get(2));
- assertEquals(sImeWindow, windows.get(1));
- assertEquals(sImeDialogWindow, windows.get(0));
-
- exitingAppWindow.removeImmediately();
+ assertForAllWindowsOrder(Arrays.asList(
+ sWallpaperWindow,
+ exitingAppWindow,
+ sChildAppWindowBelow,
+ sAppWindow,
+ sChildAppWindowAbove,
+ sDockedDividerWindow,
+ sStatusBarWindow,
+ sNavBarWindow,
+ sImeWindow,
+ sImeDialogWindow));
}
@Test
@@ -95,78 +75,49 @@
sWm.mInputMethodTarget = imeAppTarget;
- final ArrayList<WindowState> windows = new ArrayList();
+ assertForAllWindowsOrder(Arrays.asList(
+ sWallpaperWindow,
+ sChildAppWindowBelow,
+ sAppWindow,
+ sChildAppWindowAbove,
+ imeAppTarget,
+ sImeWindow,
+ sImeDialogWindow,
+ sDockedDividerWindow,
+ sStatusBarWindow,
+ sNavBarWindow));
+ }
- // Test forward traversal.
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
+ @Test
+ public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
+ sWm.mInputMethodTarget = sChildAppWindowAbove;
- assertEquals(sWallpaperWindow, windows.get(0));
- assertEquals(sChildAppWindowBelow, windows.get(1));
- assertEquals(sAppWindow, windows.get(2));
- assertEquals(sChildAppWindowAbove, windows.get(3));
- assertEquals(imeAppTarget, windows.get(4));
- assertEquals(sImeWindow, windows.get(5));
- assertEquals(sImeDialogWindow, windows.get(6));
- assertEquals(sDockedDividerWindow, windows.get(7));
- assertEquals(sStatusBarWindow, windows.get(8));
- assertEquals(sNavBarWindow, windows.get(9));
-
- // Test backward traversal.
- windows.clear();
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(9));
- assertEquals(sChildAppWindowBelow, windows.get(8));
- assertEquals(sAppWindow, windows.get(7));
- assertEquals(sChildAppWindowAbove, windows.get(6));
- assertEquals(imeAppTarget, windows.get(5));
- assertEquals(sImeWindow, windows.get(4));
- assertEquals(sImeDialogWindow, windows.get(3));
- assertEquals(sDockedDividerWindow, windows.get(2));
- assertEquals(sStatusBarWindow, windows.get(1));
- assertEquals(sNavBarWindow, windows.get(0));
-
- // Clean-up
- sWm.mInputMethodTarget = null;
- imeAppTarget.removeImmediately();
+ assertForAllWindowsOrder(Arrays.asList(
+ sWallpaperWindow,
+ sChildAppWindowBelow,
+ sAppWindow,
+ sChildAppWindowAbove,
+ sImeWindow,
+ sImeDialogWindow,
+ sDockedDividerWindow,
+ sStatusBarWindow,
+ sNavBarWindow));
}
@Test
public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
-
sWm.mInputMethodTarget = sStatusBarWindow;
- final ArrayList<WindowState> windows = new ArrayList();
-
- // Test forward traversal.
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(0));
- assertEquals(sChildAppWindowBelow, windows.get(1));
- assertEquals(sAppWindow, windows.get(2));
- assertEquals(sChildAppWindowAbove, windows.get(3));
- assertEquals(sDockedDividerWindow, windows.get(4));
- assertEquals(sStatusBarWindow, windows.get(5));
- assertEquals(sImeWindow, windows.get(6));
- assertEquals(sImeDialogWindow, windows.get(7));
- assertEquals(sNavBarWindow, windows.get(8));
-
- // Test backward traversal.
- windows.clear();
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(8));
- assertEquals(sChildAppWindowBelow, windows.get(7));
- assertEquals(sAppWindow, windows.get(6));
- assertEquals(sChildAppWindowAbove, windows.get(5));
- assertEquals(sDockedDividerWindow, windows.get(4));
- assertEquals(sStatusBarWindow, windows.get(3));
- assertEquals(sImeWindow, windows.get(2));
- assertEquals(sImeDialogWindow, windows.get(1));
- assertEquals(sNavBarWindow, windows.get(0));
-
- // Clean-up
- sWm.mInputMethodTarget = null;
+ assertForAllWindowsOrder(Arrays.asList(
+ sWallpaperWindow,
+ sChildAppWindowBelow,
+ sAppWindow,
+ sChildAppWindowAbove,
+ sDockedDividerWindow,
+ sStatusBarWindow,
+ sImeWindow,
+ sImeDialogWindow,
+ sNavBarWindow));
}
@Test
@@ -176,38 +127,35 @@
final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
sDisplayContent, "voiceInteractionWindow");
- final ArrayList<WindowState> windows = new ArrayList();
+ assertForAllWindowsOrder(Arrays.asList(
+ sWallpaperWindow,
+ sChildAppWindowBelow,
+ sAppWindow,
+ sChildAppWindowAbove,
+ sDockedDividerWindow,
+ voiceInteractionWindow,
+ sStatusBarWindow,
+ sNavBarWindow,
+ sImeWindow,
+ sImeDialogWindow));
+ }
- // Test forward traversal.
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
+ @Test
+ public void testComputeImeTarget() throws Exception {
+ // Verify that an app window can be an ime target.
+ final WindowState appWin = createWindow(null, TYPE_APPLICATION, sDisplayContent, "appWin");
+ appWin.setHasSurface(true);
+ assertTrue(appWin.canBeImeTarget());
+ WindowState imeTarget = sDisplayContent.computeImeTarget(false /* updateImeTarget */);
+ assertEquals(appWin, imeTarget);
- assertEquals(sWallpaperWindow, windows.get(0));
- assertEquals(sChildAppWindowBelow, windows.get(1));
- assertEquals(sAppWindow, windows.get(2));
- assertEquals(sChildAppWindowAbove, windows.get(3));
- assertEquals(sDockedDividerWindow, windows.get(4));
- assertEquals(voiceInteractionWindow, windows.get(5));
- assertEquals(sStatusBarWindow, windows.get(6));
- assertEquals(sNavBarWindow, windows.get(7));
- assertEquals(sImeWindow, windows.get(8));
- assertEquals(sImeDialogWindow, windows.get(9));
-
- // Test backward traversal.
- windows.clear();
- sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
- assertEquals(sWallpaperWindow, windows.get(9));
- assertEquals(sChildAppWindowBelow, windows.get(8));
- assertEquals(sAppWindow, windows.get(7));
- assertEquals(sChildAppWindowAbove, windows.get(6));
- assertEquals(sDockedDividerWindow, windows.get(5));
- assertEquals(voiceInteractionWindow, windows.get(4));
- assertEquals(sStatusBarWindow, windows.get(3));
- assertEquals(sNavBarWindow, windows.get(2));
- assertEquals(sImeWindow, windows.get(1));
- assertEquals(sImeDialogWindow, windows.get(0));
-
- voiceInteractionWindow.removeImmediately();
+ // Verify that an child window can be an ime target.
+ final WindowState childWin = createWindow(appWin,
+ TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
+ childWin.setHasSurface(true);
+ assertTrue(childWin.canBeImeTarget());
+ imeTarget = sDisplayContent.computeImeTarget(false /* updateImeTarget */);
+ assertEquals(childWin, imeTarget);
}
/**
@@ -284,4 +232,24 @@
assertEquals(currentOverrideConfig.densityDpi, globalConfig.densityDpi);
assertEquals(currentOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
}
+
+ private void assertForAllWindowsOrder(List<WindowState> expectedWindows) {
+ final LinkedList<WindowState> actualWindows = new LinkedList();
+
+ // Test forward traversal.
+ sDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
+ assertEquals(expectedWindows.size(), actualWindows.size());
+ for (WindowState w : expectedWindows) {
+ assertEquals(w, actualWindows.pollFirst());
+ }
+ assertTrue(actualWindows.isEmpty());
+
+ // Test backward traversal.
+ sDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
+ assertEquals(expectedWindows.size(), actualWindows.size());
+ for (WindowState w : expectedWindows) {
+ assertEquals(w, actualWindows.pollLast());
+ }
+ assertTrue(actualWindows.isEmpty());
+ }
}
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 df35b7ee..5f51898 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -23,10 +23,17 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import java.util.LinkedList;
+
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
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.TYPE_APPLICATION;
+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_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -167,4 +174,34 @@
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
}
+
+ @Test
+ public void testGetWindow() throws Exception {
+ final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+ final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
+ final WindowState mediaOverlayChild = createWindow(root,
+ TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild");
+ final WindowState attachedDialogChild = createWindow(root,
+ TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild");
+ final WindowState subPanelChild = createWindow(root,
+ TYPE_APPLICATION_SUB_PANEL, "subPanelChild");
+ final WindowState aboveSubPanelChild = createWindow(root,
+ TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
+
+ final LinkedList<WindowState> windows = new LinkedList();
+
+ root.getWindow(w -> {
+ windows.addLast(w);
+ return false;
+ });
+
+ // getWindow should have returned candidate windows in z-order.
+ assertEquals(aboveSubPanelChild, windows.pollFirst());
+ assertEquals(subPanelChild, windows.pollFirst());
+ assertEquals(attachedDialogChild, windows.pollFirst());
+ assertEquals(root, windows.pollFirst());
+ assertEquals(mediaOverlayChild, windows.pollFirst());
+ assertEquals(mediaChild, windows.pollFirst());
+ assertTrue(windows.isEmpty());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index e5e3512..52e10a5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -44,6 +44,7 @@
import static android.content.res.Configuration.EMPTY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -60,6 +61,7 @@
import com.android.server.AttributeCache;
import java.util.HashSet;
+import java.util.LinkedList;
/**
* Common base class for window manager unit test classes.
@@ -120,6 +122,7 @@
sCommonWindows = new HashSet();
sWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
sImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "sImeWindow");
+ sWm.mInputMethodWindow = sImeWindow;
sImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, "sImeDialogWindow");
sStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "sStatusBarWindow");
sNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "sNavBarWindow");
@@ -133,16 +136,25 @@
@After
public void tearDown() throws Exception {
+ final LinkedList<WindowState> nonCommonWindows = new LinkedList();
sWm.mRoot.forAllWindows(w -> {
if (!sCommonWindows.contains(w)) {
- w.removeImmediately();
+ nonCommonWindows.addLast(w);
}
}, true /* traverseTopToBottom */);
+
+ while (!nonCommonWindows.isEmpty()) {
+ nonCommonWindows.pollLast().removeImmediately();
+ }
+
+ sWm.mInputMethodTarget = null;
}
private static WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
sCommonWindows.add(win);
+ // Prevent common windows from been IMe targets
+ win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
return win;
}