diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 203137d4d481d..73f68f8d61b0e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2622,6 +2622,22 @@ class DisplayContent extends WindowContainer { + final LinkedList mTmpWindows = new LinkedList(); + + private final ToBooleanFunction mCollectWindowsInOrder = w -> { + int addIndex = mTmpWindows.size(); + for (int i = mTmpWindows.size() - 1; i >= 0; --i) { + final WindowState current = mTmpWindows.get(i); + if (w.mBaseLayer > current.mBaseLayer) { + break; + } + addIndex = i; + } + + mTmpWindows.add(addIndex, w); + return false; + }; + /** * Compares two child window tokens returns -1 if the first is lesser than the second in * terms of z-order and 1 otherwise. @@ -2652,6 +2668,29 @@ class DisplayContent extends WindowContainer callback, + boolean traverseTopToBottom) { + // Hack to work around WindowToken containing windows of various types there by causing + // the windows not to be returned in visual order if there is another token with a + // window that should be z-order in-between the windows of the first token. This is an + // issue due to the various window types sys-ui adds with its token. + // TODO: Have a separate token for each type of window sys-ui wants to add. Would + // require some changes to sys-ui on the token it uses for window creation vs. just + // using the default token of its process. + mTmpWindows.clear(); + super.forAllWindows(mCollectWindowsInOrder, false /* traverseTopToBottom */); + + while(!mTmpWindows.isEmpty()) { + final WindowState current = traverseTopToBottom + ? mTmpWindows.pollLast() : mTmpWindows.pollFirst(); + if (callback.apply(current)) { + return true; + } + } + return false; + } + @Override int getOrientation() { final WindowManagerPolicy policy = mService.mPolicy; 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 162a1a90875a2..0e20c4e222534 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -26,6 +26,7 @@ import android.support.test.runner.AndroidJUnit4; import java.util.ArrayList; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static org.junit.Assert.assertEquals; /** @@ -77,6 +78,8 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(sNavBarWindow, windows.get(2)); assertEquals(sImeWindow, windows.get(1)); assertEquals(sImeDialogWindow, windows.get(0)); + + exitingAppWindow.removeImmediately(); } @Test @@ -121,4 +124,45 @@ public class DisplayContentTests extends WindowTestsBase { sWm.mInputMethodTarget = null; imeAppTarget.removeImmediately(); } + + @Test + public void testForAllWindows_WithInBetweenWindowToken() throws Exception { + // This window is set-up to be z-ordered between some windows that go in the same token like + // the nav bar and status bar. + final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION, + sDisplayContent, "voiceInteractionWindow"); + + final ArrayList 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(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(); + } } 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 ab6968b6c1492..d945d207a4c67 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -82,10 +82,11 @@ public class WindowTestsBase { sImeDialogWindow = createWindow(null, TYPE_INPUT_METHOD_DIALOG, sDisplayContent, "sImeDialogWindow"); sStatusBarWindow = createWindow(null, TYPE_STATUS_BAR, sDisplayContent, "sStatusBarWindow"); + final WindowToken statusBarToken = sStatusBarWindow.mToken; sNavBarWindow = - createWindow(null, TYPE_NAVIGATION_BAR, sStatusBarWindow.mToken, "sNavBarWindow"); + createWindow(null, TYPE_NAVIGATION_BAR, statusBarToken, "sNavBarWindow"); sDockedDividerWindow = - createWindow(null, TYPE_DOCK_DIVIDER, sDisplayContent, "sDockedDividerWindow"); + createWindow(null, TYPE_DOCK_DIVIDER, statusBarToken, "sDockedDividerWindow"); sAppWindow = createWindow(null, TYPE_BASE_APPLICATION, sDisplayContent, "sAppWindow"); sChildAppWindowAbove = createWindow(sAppWindow, TYPE_APPLICATION_ATTACHED_DIALOG, sAppWindow.mToken, "sChildAppWindowAbove");