diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index d46b535386437..a3f7ac6d6c0d4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -676,6 +676,34 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mPendingRelaunchCount = 0; } + /** + * Returns true if the new child window we are adding to this token is considered greater than + * the existing child window in this token in terms of z-order. + */ + @Override + protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, + WindowState existingWindow) { + final int type1 = newWindow.mAttrs.type; + final int type2 = existingWindow.mAttrs.type; + + // Base application windows should be z-ordered BELOW all other windows in the app token. + if (type1 == TYPE_BASE_APPLICATION && type2 != TYPE_BASE_APPLICATION) { + return false; + } else if (type1 != TYPE_BASE_APPLICATION && type2 == TYPE_BASE_APPLICATION) { + return true; + } + + // Starting windows should be z-ordered ABOVE all other windows in the app token. + if (type1 == TYPE_APPLICATION_STARTING && type2 != TYPE_APPLICATION_STARTING) { + return true; + } else if (type1 != TYPE_APPLICATION_STARTING && type2 == TYPE_APPLICATION_STARTING) { + return false; + } + + // Otherwise the new window is greater than the existing window. + return true; + } + @Override void addWindow(WindowState w) { super.addWindow(w); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7cd99714c00cb..0b39d65c9407b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1085,12 +1085,13 @@ class DisplayContent extends WindowContainer { // The display this token is on. private DisplayContent mDisplayContent; + /** + * Compares two child window of this token and returns -1 if the first is lesser than the + * second in terms of z-order and 1 otherwise. + */ + private final Comparator mWindowComparator = + (WindowState newWindow, WindowState existingWindow) -> { + final WindowToken token = WindowToken.this; + if (newWindow.mToken != token) { + throw new IllegalArgumentException("newWindow=" + newWindow + + " is not a child of token=" + token); + } + + if (existingWindow.mToken != token) { + throw new IllegalArgumentException("existingWindow=" + existingWindow + + " is not a child of token=" + token); + } + + return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1; + }; + WindowToken(WindowManagerService service, IBinder _token, int type, boolean _explicit, DisplayContent dc) { mService = service; @@ -168,19 +189,31 @@ class WindowToken extends WindowContainer { return -1; } + /** + * Returns true if the new window is considered greater than the existing window in terms of + * z-order. + */ + protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, + WindowState existingWindow) { + // By default the first window isn't greater than the second to preserve existing logic of + // how new windows are added to the token + return false; + } + void addWindow(final WindowState win) { - if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindow: win=" + win + " Callers=" + Debug.getCallers(5)); + if (DEBUG_FOCUS) Slog.d(TAG_WM, + "addWindow: win=" + win + " Callers=" + Debug.getCallers(5)); if (!win.isChildWindow()) { - int tokenWindowsPos = 0; if (asAppWindowToken() != null) { - tokenWindowsPos = mDisplayContent.addAppWindowToWindowList(win); + 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, tokenWindowsPos); + addChild(win, mWindowComparator); } } else { mDisplayContent.addChildWindowToWindowList(win); diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 74e8c9d52836e..d933cb377ef08 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -29,17 +29,19 @@ import android.view.IWindow; import android.view.WindowManager; 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_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * Tests for the {@link WindowState} class. * - * Build: mmma -j32 frameworks/base/services/tests/servicestests - * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk - * Run: adb shell am instrument -w -e class com.android.server.wm.AppWindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests */ @SmallTest @Presubmit @@ -55,6 +57,36 @@ public class AppWindowTokenTests { sWm = TestWindowManagerPolicy.getWindowManagerService(context); } + @Test + public void testAddWindow_Order() throws Exception { + final TestAppWindowToken token = new TestAppWindowToken(); + + assertEquals(0, token.getWindowsCount()); + + final WindowState win1 = createWindow(null, TYPE_APPLICATION, token); + final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token); + final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token); + final WindowState win4 = createWindow(null, TYPE_APPLICATION, token); + + token.addWindow(win1); + token.addWindow(startingWin); + token.addWindow(baseWin); + token.addWindow(win4); + + // Should not contain the windows that were added above. + assertEquals(4, token.getWindowsCount()); + assertTrue(token.hasWindow(win1)); + assertTrue(token.hasWindow(startingWin)); + assertTrue(token.hasWindow(baseWin)); + assertTrue(token.hasWindow(win4)); + + // The starting window should be on-top of all other windows. + assertEquals(startingWin, token.getLastChild()); + + // The base application window should be below all other windows. + assertEquals(baseWin, token.getFirstChild()); + } + @Test public void testFindMainWindow() throws Exception { final TestAppWindowToken token = new TestAppWindowToken(); @@ -93,5 +125,13 @@ public class AppWindowTokenTests { boolean hasWindow(WindowState w) { return mChildren.contains(w); } + + WindowState getFirstChild() { + return mChildren.getFirst(); + } + + WindowState getLastChild() { + return mChildren.getLast(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java index 546c7da0a013e..5326a192bf73f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -41,9 +41,8 @@ import static org.mockito.Mockito.mock; /** * Tests for the {@link WindowState} class. * - * Build: mmma -j32 frameworks/base/services/tests/servicestests - * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk - * Run: adb shell am instrument -w -e class com.android.server.wm.WindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests */ @SmallTest @Presubmit