Re-implement Z-ordering based off SurfaceControl hierarchy.
First steps in using the SurfaceControl hierarchy pervasively through
the WindowManager. For each level of the WindowContainer hierarchy
we instantiate a buffer-less SurfaceControl which is used to apply
constraints to all of it's children. For now we use full-screen surfaces
at every non-leaf level and so the only constraint we apply is Z-ordering.
Calculation of Z-ordering is done through the WindowContainer#assignLayers
pass and begins with DisplayContent. Changes are accumulated in-to a pending
transaction which is applied as part of prepareSurfaces (which now applies
to each level of the WindowContainer hierarchy rather than just windows).
Bug: 64815723
Test: Existing tests pass. go/wm-smoke. ZOrderTests.
Change-Id: Ib31bc3107c7fa398cf2ed72430fcb7596fad6fd5
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76ab..9a5ebed 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 6060881..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@
controller.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+ controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+ mDisplayContent.onPendingTransactionApplied();
}
}
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 bb88264..d9ab5c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@
@RunWith(AndroidJUnit4.class)
public class AppWindowTokenTests extends WindowTestsBase {
+ TaskStack mStack;
+ Task mTask;
+ WindowTestUtils.TestAppWindowToken mToken;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mStack = createTaskStackOnDisplay(mDisplayContent);
+ mTask = createTaskInStack(mStack, 0 /* userId */);
+ mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+ mTask.addChild(mToken, 0);
+ }
+
@Test
@Presubmit
public void testAddWindow_Order() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertEquals(0, mToken.getWindowsCount());
- assertEquals(0, token.getWindowsCount());
-
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "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));
+ assertEquals(4, mToken.getWindowsCount());
+ assertTrue(mToken.hasWindow(win1));
+ assertTrue(mToken.hasWindow(startingWin));
+ assertTrue(mToken.hasWindow(baseWin));
+ assertTrue(mToken.hasWindow(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, token.getLastChild());
+ assertEquals(startingWin, mToken.getLastChild());
// The base application window should be below all other windows.
- assertEquals(baseWin, token.getFirstChild());
- token.removeImmediately();
+ assertEquals(baseWin, mToken.getFirstChild());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.findMainWindow());
- assertNull(token.findMainWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- assertEquals(window1, token.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+ assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, token.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
- assertEquals(window2, token.findMainWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ assertEquals(window2, mToken.findMainWindow());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.getTopFullscreenWindow());
- assertNull(token.getTopFullscreenWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
- assertEquals(window12, token.getTopFullscreenWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+ assertEquals(window12, mToken.getTopFullscreenWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, token.getTopFullscreenWindow());
+ assertEquals(window11, mToken.getTopFullscreenWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, token.getTopFullscreenWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.getTopFullscreenWindow());
+ mToken.removeImmediately();
}
@Test
@@ -124,27 +130,21 @@
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.resizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
- final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@
@Test
@Presubmit
public void testGetOrientation() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- token.setFillsParent(false);
+ mToken.setFillsParent(false);
// Can specify orientation if app doesn't fill parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- token.setFillsParent(true);
- token.hidden = true;
- token.sendingToBottom = true;
+ mToken.setFillsParent(true);
+ mToken.hidden = true;
+ mToken.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it fills parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
- token.sendingToBottom = false;
- token.setIsOnTop(true);
- // Allow for token to provide orientation hidden if on top and not being sent to bottom.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ mToken.sendingToBottom = false;
+ mToken.setIsOnTop(true);
+ // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
}
@Test
@Presubmit
public void testKeyguardFlagsDuringRelaunch() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
// Add window with show when locked flag
- token.addWindow(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.addWindow(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Start relaunching
- token.startRelaunching();
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.startRelaunching();
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- token.removeChild(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.removeChild(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- token.finishRelaunching();
- assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+ mToken.finishRelaunching();
+ assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def7..873a01b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
mPositioner = new TaskPositioner(sWm);
- mPositioner.register(display);
+ mPositioner.register(mDisplayContent);
}
/**
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 53d0bfb..a45695f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -568,11 +568,6 @@
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- return false;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-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_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
- @Test
- public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
- sWm.mInputMethodTarget = null;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The Ime has an higher base layer than app windows and lower base layer than system
- // windows, so it should be above app windows and below system windows if there isn't an IME
- // target.
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows and below system windows if it is targeting an app
- // window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
- "imeAppTargetChildAboveWindow");
- final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
- "imeAppTargetChildBelowWindow");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for child windows that are z-ordered above it
- // and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for non-fullscreen app window above it and
- // below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
- final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
- mDisplayContent, "imeSystemOverlayTarget",
- true /* ownerCanAddInternalSystemWindow */);
-
- sWm.mInputMethodTarget = imeSystemOverlayTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The IME target base layer is higher than all window except for the nav bar window, so the
- // IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testStackLayers() throws Exception {
- WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
- WindowState dockedStackWindow = createWindowOnStack(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "dockedStackWindow");
- WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
-
- mLayersController.assignWindowLayers(mDisplayContent);
-
- assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
- }
-
- private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
- throws Exception {
- assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
- }
-
-}
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 0980f7e..4c5e291 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
- WindowLayersController mLayersController;
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
+
sWm = TestWindowManagerPolicy.getWindowManagerService(context);
- mLayersController = new WindowLayersController(sWm);
+ beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@
waitUntilHandlersIdle();
}
+ void beforeCreateDisplay() {
+ // Called before display is created.
+ }
+
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@
waitUntilHandlersIdle();
}
+ /**
+ * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+ * set-up (or null).
+ */
+ SurfaceBuilderFactory getSurfaceBuilderFactory() {
+ return null;
+ }
+
private WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
mCommonWindows.add(win);
@@ -162,6 +174,11 @@
Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
}
+ /** Asserts that the first entry is greater than the second entry. */
+ void assertLessThan(int first, int second) throws Exception {
+ Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+ }
+
/**
* Waits until the main handler for WM has processed all messages.
*/
@@ -264,7 +281,7 @@
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+ return new DisplayContent(display, sWm, new WallpaperController(sWm));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
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 692e08b..7219104 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@
assertEquals(null, dc.getWindowToken(token.token));
}
- @Test
- public void testAdjustAnimLayer() throws Exception {
- final WindowTestUtils.TestWindowToken token =
- new WindowTestUtils.TestWindowToken(0, mDisplayContent);
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
- final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
- window2.mLayer = 100;
- window3.mLayer = 200;
-
- // We assign layers once, to get the base values computed by
- // the controller.
- mLayersController.assignWindowLayers(mDisplayContent);
-
- final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
- final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
- final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
- final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
- final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
- // Then we set an adjustment, and assign them again, they should
- // be offset.
- int adj = token.adj = 50;
- mLayersController.assignWindowLayers(mDisplayContent);
- final int highestLayer = token.getHighestAnimLayer();
-
- assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
- assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
- assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
- assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, highestLayer);
- }
-
/**
* Test that a window token isn't orphaned by the system when it is requested to be removed.
* Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 0000000..f7c4b1f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+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_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+ private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+ HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+ HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+ @Override
+ public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+ mRelativeLayersForControl.remove(sc);
+ mLayersForControl.put(sc, layer);
+ return super.setLayer(sc, layer);
+ }
+
+ @Override
+ public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+ SurfaceControl relativeTo,
+ int layer) {
+ mRelativeLayersForControl.put(sc, relativeTo);
+ mLayersForControl.put(sc, layer);
+ return super.setRelativeLayer(sc, relativeTo, layer);
+ }
+
+ int getLayer(SurfaceControl sc) {
+ return mLayersForControl.get(sc);
+ }
+
+ SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ return mRelativeLayersForControl.get(sc);
+ }
+ };
+
+ // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+ // such that we can keep track of the parents of Surfaces as they are constructed.
+ private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+ private class HierarchyRecorder extends SurfaceControl.Builder {
+ SurfaceControl mPendingParent;
+
+ HierarchyRecorder(SurfaceSession s) {
+ super(s);
+ }
+
+ public SurfaceControl.Builder setParent(SurfaceControl sc) {
+ mPendingParent = sc;
+ return super.setParent(sc);
+ }
+ public SurfaceControl build() {
+ SurfaceControl sc = super.build();
+ mParentFor.put(sc, mPendingParent);
+ mPendingParent = null;
+ return sc;
+ }
+ };
+
+ class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new HierarchyRecorder(s);
+ }
+ };
+
+ private LayerRecordingTransaction mTransaction;
+
+ @Override
+ void beforeCreateDisplay() {
+ // We can't use @Before here because it may happen after WindowTestsBase @Before
+ // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+ // would miss construction of the top-level layers.
+ mTransaction = new LayerRecordingTransaction();
+ sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+ }
+
+ @After
+ public void after() {
+ mTransaction.close();
+ mParentFor.clear();
+ }
+
+ LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+ LinkedList<SurfaceControl> p = new LinkedList();
+ SurfaceControl current = sc;
+ do {
+ p.addLast(current);
+
+ SurfaceControl rs = t.getRelativeLayer(current);
+ if (rs != null) {
+ current = rs;
+ } else {
+ current = mParentFor.get(current);
+ }
+ } while (current != null);
+ return p;
+ }
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t,
+ SurfaceControl left, SurfaceControl right) throws Exception {
+ final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+ final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+ SurfaceControl commonAncestor = null;
+ SurfaceControl leftTop = leftParentChain.peekLast();
+ SurfaceControl rightTop = rightParentChain.peekLast();
+ while (leftTop != null && rightTop != null && leftTop == rightTop) {
+ commonAncestor = leftParentChain.removeLast();
+ rightParentChain.removeLast();
+ leftTop = leftParentChain.peekLast();
+ rightTop = rightParentChain.peekLast();
+ }
+
+ if (rightTop == null) { // right is the parent of left.
+ assertGreaterThan(t.getLayer(leftTop), 0);
+ } else if (leftTop == null) { // left is the parent of right.
+ assertGreaterThan(0, t.getLayer(rightTop));
+ } else {
+ assertGreaterThan(t.getLayer(leftTop),
+ t.getLayer(rightTop));
+ }
+ }
+
+ void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+ WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+ sWm.mInputMethodTarget = null;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The Ime has an higher base layer than app windows and lower base layer than system
+ // windows, so it should be above app windows and below system windows if there isn't an IME
+ // target.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows and below system windows if it is targeting an app
+ // window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+ "imeAppTargetChildAboveWindow");
+ final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+ "imeAppTargetChildBelowWindow");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for child windows that are z-ordered above it
+ // and below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+ final WindowState appBelowImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState appAboveImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for non-fullscreen app window above it and
+ // below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+ assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+ final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+ mDisplayContent, "imeSystemOverlayTarget",
+ true /* ownerCanAddInternalSystemWindow */);
+
+ sWm.mInputMethodTarget = imeSystemOverlayTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The IME target base layer is higher than all window except for the nav bar window, so the
+ // IME should be above all windows except for the nav bar.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+ // The IME has a higher base layer than the status bar so we may expect it to go
+ // above the status bar once they are both in the Non-App layer, as past versions of this
+ // test enforced. However this seems like the wrong behavior unless the status bar is the
+ // IME target.
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+ sWm.mInputMethodTarget = mStatusBarWindow;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testStackLayers() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState dockedStackWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "dockedStackWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+ assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ }
+}