diff options
9 files changed, 339 insertions, 230 deletions
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java index c06e5cc6d7ad..32373f901be3 100644 --- a/services/core/java/com/android/server/wm/WindowLayersController.java +++ b/services/core/java/com/android/server/wm/WindowLayersController.java @@ -23,6 +23,7 @@ import java.util.ArrayDeque; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -63,17 +64,19 @@ class WindowLayersController { private int mCurBaseLayer; private int mCurLayer; private boolean mAnyLayerChanged; + private int mHighestLayerInImeTargetBaseLayer; + private WindowState mImeTarget; final void assignWindowLayers(DisplayContent dc) { if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based", new RuntimeException("here").fillInStackTrace()); - clear(); + reset(); dc.forAllWindows((w) -> { boolean layerChanged = false; int oldLayer = w.mLayer; - if (w.mBaseLayer == mCurBaseLayer || w.mIsImWindow) { + if (w.mBaseLayer == mCurBaseLayer) { mCurLayer += WINDOW_LAYER_MULTIPLIER; } else { mCurBaseLayer = mCurLayer = w.mBaseLayer; @@ -92,6 +95,11 @@ class WindowLayersController { mHighestApplicationLayer = Math.max(mHighestApplicationLayer, w.mWinAnimator.mAnimLayer); } + if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) { + mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer, + w.mWinAnimator.mAnimLayer); + } + collectSpecialWindows(w); if (layerChanged) { @@ -103,7 +111,7 @@ class WindowLayersController { //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && mAnyLayerChanged - && dc.getDisplayId() == Display.DEFAULT_DISPLAY) { + && dc.getDisplayId() == DEFAULT_DISPLAY) { mService.mAccessibilityController.onWindowLayersChangedLocked(); } @@ -120,7 +128,7 @@ class WindowLayersController { }, false /* traverseTopToBottom */); } - private void clear() { + private void reset() { mHighestApplicationLayer = 0; mPinnedWindows.clear(); mInputMethodWindows.clear(); @@ -132,6 +140,9 @@ class WindowLayersController { mCurBaseLayer = 0; mCurLayer = 0; mAnyLayerChanged = false; + + mImeTarget = mService.mInputMethodTarget; + mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0; } private void collectSpecialWindows(WindowState w) { @@ -174,22 +185,10 @@ class WindowLayersController { layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer); - boolean onTopLauncherVisible = !mOnTopLauncherWindows.isEmpty(); while (!mOnTopLauncherWindows.isEmpty()) { layer = assignAndIncreaseLayerIfNeeded(mOnTopLauncherWindows.remove(), layer); } - // Make sure IME windows are showing above the dock divider and on-top launcher windows. - if ((mDockDivider != null && mDockDivider.isVisibleLw()) || onTopLauncherVisible) { - while (!mInputMethodWindows.isEmpty()) { - final WindowState w = mInputMethodWindows.remove(); - // Only ever move IME windows up, else we brake IME for windows above the divider. - if (layer > w.mLayer) { - layer = assignAndIncreaseLayerIfNeeded(w, layer); - } - } - } - // We know that we will be animating a relaunching window in the near future, which will // receive a z-order increase. We want the replaced window to immediately receive the same // treatment, e.g. to be above the dock divider. @@ -200,12 +199,26 @@ class WindowLayersController { while (!mPinnedWindows.isEmpty()) { layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer); } + + // Make sure IME is the highest window in the base layer of it's target. + if (mImeTarget != null) { + if (mImeTarget.mAppToken == null) { + // For non-app ime targets adjust the layer we start from to match what we found + // when assigning layers. Otherwise, just use the highest app layer we have some far. + layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER; + } + + while (!mInputMethodWindows.isEmpty()) { + layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer); + } + } + } private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) { if (win != null) { assignAnimLayer(win, layer); - // Make sure we leave space inbetween normal windows for dims and such. + // Make sure we leave space in-between normal windows for dims and such. layer += WINDOW_LAYER_MULTIPLIER; } return layer; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c11003dd972b..661df9cd60ad 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1905,9 +1905,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } int getAnimLayerAdjustment() { - final boolean isImeType = - mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; - if (isImeType && mService.mInputMethodTarget != null) { + if (mIsImWindow && mService.mInputMethodTarget != null) { final AppWindowToken appToken = mService.mInputMethodTarget.mAppToken; if (appToken != null) { return appToken.mAppAnimator.animLayerAdjustment; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 402bcfb863f0..a2eebc336970 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -176,21 +176,6 @@ class WindowToken extends WindowContainer<WindowState> { } /** - * Recursive search through a WindowList and all of its windows' children. - * @param target The window to search for. - * @return The index of win in windows or of the window that is an ancestor of win. - */ - int getWindowIndex(WindowState target) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowState w = mChildren.get(i); - if (w == target || w.hasChild(target)) { - return i; - } - } - return -1; - } - - /** * Returns true if the new window is considered greater than the existing window in terms of * z-order. */ 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 772b2a2c4737..207939fc9dd9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -45,16 +45,7 @@ import static org.junit.Assert.assertTrue; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class AppWindowTokenTests { - - private static WindowManagerService sWm = null; - private final IWindow mIWindow = new TestIWindow(); - - @Before - public void setUp() throws Exception { - final Context context = InstrumentationRegistry.getTargetContext(); - sWm = TestWindowManagerPolicy.getWindowManagerService(context); - } +public class AppWindowTokenTests extends WindowTestsBase { @Test public void testAddWindow_Order() throws Exception { @@ -62,10 +53,11 @@ public class AppWindowTokenTests { 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); + final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1"); + final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token, + "startingWin"); + final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin"); + final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4"); token.addWindow(win1); token.addWindow(startingWin); @@ -92,24 +84,18 @@ public class AppWindowTokenTests { assertNull(token.findMainWindow()); - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token); + 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"); token.addWindow(window1); assertEquals(window1, token.findMainWindow()); window1.mAnimatingExit = true; assertEquals(window1, token.findMainWindow()); - final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token); + final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2"); token.addWindow(window2); assertEquals(window2, token.findMainWindow()); } - private WindowState createWindow(WindowState parent, int type, WindowToken token) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); - - return new WindowState(sWm, null, mIWindow, token, parent, 0, 0, attrs, 0, 0); - } - /* Used so we can gain access to some protected members of the {@link AppWindowToken} class */ private class TestAppWindowToken extends AppWindowToken { 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 225dc5d2d66a..0801a883177d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -16,28 +16,15 @@ package com.android.server.wm; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import android.content.Context; -import android.content.res.Configuration; -import android.os.IBinder; import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.Display; -import android.view.IWindow; -import android.view.WindowManager; import java.util.ArrayList; -import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; -import static android.app.AppOpsManager.OP_NONE; -import static android.content.res.Configuration.EMPTY; -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; -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; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -47,7 +34,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; /** * Tests for the {@link DisplayContent} class. @@ -58,21 +44,7 @@ import static org.mockito.Mockito.mock; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class DisplayContentTests { - - private static WindowManagerService sWm = null; - private final IWindow mIWindow = new TestIWindow(); - private final Session mMockSession = mock(Session.class); - private Display mDisplay; - private int mNextStackId = FIRST_DYNAMIC_STACK_ID; - private int mNextTaskId = 0; - - @Before - public void setUp() throws Exception { - final Context context = InstrumentationRegistry.getTargetContext(); - sWm = TestWindowManagerPolicy.getWindowManagerService(context); - mDisplay = context.getDisplay(); - } +public class DisplayContentTests extends WindowTestsBase { @Test public void testForAllWindows() throws Exception { @@ -124,36 +96,4 @@ public class DisplayContentTests { assertEquals(imeWindow, windows.get(1)); assertEquals(imeDialogWindow, windows.get(0)); } - - private WindowToken createWindowToken(DisplayContent dc, int type) { - if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { - return new WindowToken(sWm, mock(IBinder.class), type, false, dc); - } - - final int stackId = mNextStackId++; - dc.addStackToDisplay(stackId, true); - final TaskStack stack = sWm.mStackIdToStack.get(stackId); - final Task task = new Task(mNextTaskId++, stack, 0, sWm, null, EMPTY, false); - stack.addTask(task, true); - final AppWindowToken token = new AppWindowToken(sWm, null, false, dc); - task.addAppToken(0, token, 0, false); - return token; - } - - private WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { - final WindowToken token = createWindowToken(dc, type); - return createWindow(parent, type, token, name); - } - - private WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); - attrs.setTitle(name); - - final WindowState w = new WindowState(sWm, mMockSession, mIWindow, token, parent, OP_NONE, - 0, attrs, 0, 0); - // TODO: Probably better to make this call in the WindowState ctor to avoid errors with - // adding it to the token... - token.addWindow(w); - return w; - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java new file mode 100644 index 000000000000..5a035d636443 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java @@ -0,0 +1,149 @@ +/* + * 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.Before; +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.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + +/** + * 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 { + + private static boolean sOneTimeSetupDone = false; + private static WindowLayersController sLayersController; + private static DisplayContent sDisplayContent; + private static WindowState sImeWindow; + private static WindowState sImeDialogWindow; + private static WindowState sStatusBarWindow; + private static WindowState sDockedDividerWindow; + private static WindowState sNavBarWindow; + private static WindowState sAppWindow; + private static WindowState sChildAppWindow; + + @Before + public void setUp() throws Exception { + super.setUp(); + + if (sOneTimeSetupDone) { + return; + } + sOneTimeSetupDone = true; + sLayersController = new WindowLayersController(sWm); + sDisplayContent = + new DisplayContent(mDisplay, sWm, sLayersController, new WallpaperController(sWm)); + final WindowState wallpaperWindow = + createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow"); + sImeWindow = createWindow(null, TYPE_INPUT_METHOD, sDisplayContent, "sImeWindow"); + sImeDialogWindow = + createWindow(null, TYPE_INPUT_METHOD_DIALOG, sDisplayContent, "sImeDialogWindow"); + sStatusBarWindow = createWindow(null, TYPE_STATUS_BAR, sDisplayContent, "sStatusBarWindow"); + sNavBarWindow = + createWindow(null, TYPE_NAVIGATION_BAR, sStatusBarWindow.mToken, "sNavBarWindow"); + sDockedDividerWindow = + createWindow(null, TYPE_DOCK_DIVIDER, sDisplayContent, "sDockedDividerWindow"); + sAppWindow = createWindow(null, TYPE_BASE_APPLICATION, sDisplayContent, "sAppWindow"); + sChildAppWindow = createWindow(sAppWindow, + TYPE_APPLICATION_ATTACHED_DIALOG, sAppWindow.mToken, "sChildAppWindow"); + } + + @Test + public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception { + sWm.mInputMethodTarget = null; + sLayersController.assignWindowLayers(sDisplayContent); + + // 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(sImeWindow, sChildAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sDockedDividerWindow); + assertWindowLayerGreaterThan(sNavBarWindow, sImeWindow); + assertWindowLayerGreaterThan(sStatusBarWindow, sImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowLayerGreaterThan(sImeDialogWindow, sImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { + final WindowState imeAppTarget = + createWindow(null, TYPE_BASE_APPLICATION, sDisplayContent, "imeAppTarget"); + sWm.mInputMethodTarget = imeAppTarget; + sLayersController.assignWindowLayers(sDisplayContent); + + // Ime should be above all app windows and below system windows if it is targeting an app + // window. + assertWindowLayerGreaterThan(sImeWindow, imeAppTarget); + assertWindowLayerGreaterThan(sImeWindow, sChildAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sDockedDividerWindow); + assertWindowLayerGreaterThan(sNavBarWindow, sImeWindow); + assertWindowLayerGreaterThan(sStatusBarWindow, sImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowLayerGreaterThan(sImeDialogWindow, sImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception { + final WindowState imeSystemOverlayTarget = + createWindow(null, TYPE_SYSTEM_OVERLAY, sDisplayContent, "imeSystemOverlayTarget"); + + sWm.mInputMethodTarget = imeSystemOverlayTarget; + sLayersController.assignWindowLayers(sDisplayContent); + + // 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(sImeWindow, imeSystemOverlayTarget); + assertWindowLayerGreaterThan(sImeWindow, sChildAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sAppWindow); + assertWindowLayerGreaterThan(sImeWindow, sDockedDividerWindow); + assertWindowLayerGreaterThan(sImeWindow, sStatusBarWindow); + assertWindowLayerGreaterThan(sNavBarWindow, sImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowLayerGreaterThan(sImeDialogWindow, sImeWindow); + } + + 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/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 4499275e8f46..50e5a2220ee3 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -46,25 +46,25 @@ import static org.junit.Assert.assertTrue; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class WindowStateTests { +public class WindowStateTests extends WindowTestsBase { - private static WindowManagerService sWm = null; private WindowToken mWindowToken; - private final IWindow mIWindow = new TestIWindow(); @Before public void setUp() throws Exception { - final Context context = InstrumentationRegistry.getTargetContext(); - sWm = TestWindowManagerPolicy.getWindowManagerService(context); + super.setUp(); mWindowToken = new WindowToken(sWm, new Binder(), 0, false, sWm.getDefaultDisplayContentLocked()); } @Test public void testIsParentWindowHidden() throws Exception { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW); + final WindowState parentWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow"); + final WindowState child1 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1"); + final WindowState child2 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2"); assertFalse(parentWindow.mHidden); assertFalse(parentWindow.isParentWindowHidden()); @@ -79,10 +79,14 @@ public class WindowStateTests { @Test public void testIsChildWindow() throws Exception { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION); + final WindowState parentWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow"); + final WindowState child1 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1"); + final WindowState child2 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2"); + final WindowState randomWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "randomWindow"); assertFalse(parentWindow.isChildWindow()); assertTrue(child1.isChildWindow()); @@ -92,12 +96,13 @@ public class WindowStateTests { @Test public void testHasChild() throws Exception { - final WindowState win1 = createWindow(null, TYPE_APPLICATION); - final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW); - final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW); - final WindowState win2 = createWindow(null, TYPE_APPLICATION); - final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION); + final WindowState win1 = createWindow(null, TYPE_APPLICATION, mWindowToken, "win1"); + final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, mWindowToken, "win11"); + final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, mWindowToken, "win12"); + final WindowState win2 = createWindow(null, TYPE_APPLICATION, mWindowToken, "win2"); + final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, mWindowToken, "win21"); + final WindowState randomWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "randomWindow"); assertTrue(win1.hasChild(win11)); assertTrue(win1.hasChild(win12)); @@ -113,23 +118,28 @@ public class WindowStateTests { @Test public void testGetBottomChild() throws Exception { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION); + final WindowState parentWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow"); assertNull(parentWindow.getBottomChild()); - final WindowState child1 = createWindow(parentWindow, TYPE_APPLICATION_PANEL); + final WindowState child1 = + createWindow(parentWindow, TYPE_APPLICATION_PANEL, mWindowToken, "child1"); assertEquals(child1, parentWindow.getBottomChild()); - final WindowState child2 = createWindow(parentWindow, TYPE_APPLICATION_PANEL); + final WindowState child2 = + createWindow(parentWindow, TYPE_APPLICATION_PANEL, mWindowToken, "child2"); // Since child1 and child2 are at the same layer, then child2 is expect to be added on top // on child1 assertEquals(child1, parentWindow.getBottomChild()); - final WindowState child3 = createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY); + final WindowState child3 = + createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY, mWindowToken, "child3"); // Since child3 is a negative layer, we would expect it to be added below current children // with positive layers. assertEquals(child3, parentWindow.getBottomChild()); - final WindowState child4 = createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY); + final WindowState child4 = + createWindow(parentWindow, TYPE_APPLICATION_MEDIA_OVERLAY, mWindowToken, "child4"); // We would also expect additional negative layers to be added below existing negative // layers. assertEquals(child4, parentWindow.getBottomChild()); @@ -137,9 +147,12 @@ public class WindowStateTests { @Test public void testGetParentWindow() throws Exception { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW); + final WindowState parentWindow = + createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow"); + final WindowState child1 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1"); + final WindowState child2 = + createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2"); assertNull(parentWindow.getParentWindow()); assertEquals(parentWindow, child1.getParentWindow()); @@ -148,9 +161,9 @@ public class WindowStateTests { @Test public void testGetTopParentWindow() throws Exception { - final WindowState root = createWindow(null, TYPE_APPLICATION); - final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW); - final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW); + final WindowState root = createWindow(null, TYPE_APPLICATION, mWindowToken, "root"); + final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, mWindowToken, "child1"); + final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, mWindowToken, "child2"); assertEquals(root, root.getTopParentWindow()); assertEquals(root, child1.getTopParentWindow()); @@ -160,16 +173,10 @@ public class WindowStateTests { @Test public void testIsOnScreen_hiddenByPolicy() { - final WindowState window = createWindow(null, TYPE_APPLICATION); + final WindowState window = createWindow(null, TYPE_APPLICATION, mWindowToken, "window"); window.setHasSurface(true); assertTrue(window.isOnScreen()); window.hideLw(false /* doAnimation */); assertFalse(window.isOnScreen()); } - - private WindowState createWindow(WindowState parent, int type) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); - - return new WindowState(sWm, null, mIWindow, mWindowToken, parent, 0, 0, attrs, 0, 0); - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java new file mode 100644 index 000000000000..9681bd2f5947 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -0,0 +1,90 @@ +/* + * 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.Assert; +import org.junit.Before; + +import android.content.Context; +import android.os.IBinder; +import android.support.test.InstrumentationRegistry; +import android.view.Display; +import android.view.IWindow; +import android.view.WindowManager; + +import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; +import static android.app.AppOpsManager.OP_NONE; +import static android.content.res.Configuration.EMPTY; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static org.mockito.Mockito.mock; + +/** + * Common base class for window manager unit test classes. + */ +public class WindowTestsBase { + static WindowManagerService sWm = null; + private final IWindow mIWindow = new TestIWindow(); + private final Session mMockSession = mock(Session.class); + Display mDisplay; + private static int sNextStackId = FIRST_DYNAMIC_STACK_ID; + private static int sNextTaskId = 0; + + @Before + public void setUp() throws Exception { + final Context context = InstrumentationRegistry.getTargetContext(); + sWm = TestWindowManagerPolicy.getWindowManagerService(context); + mDisplay = context.getDisplay(); + } + + /** Asserts that the first entry is greater than the second entry. */ + void assertGreaterThan(int first, int second) throws Exception { + Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second); + } + + WindowToken createWindowToken(DisplayContent dc, int type) { + if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { + return new WindowToken(sWm, mock(IBinder.class), type, false, dc); + } + + final int stackId = sNextStackId++; + dc.addStackToDisplay(stackId, true); + final TaskStack stack = sWm.mStackIdToStack.get(stackId); + final Task task = new Task(sNextTaskId++, stack, 0, sWm, null, EMPTY, false); + stack.addTask(task, true); + final AppWindowToken token = new AppWindowToken(sWm, null, false, dc); + task.addAppToken(0, token, 0, false); + return token; + } + + WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { + final WindowToken token = createWindowToken(dc, type); + return createWindow(parent, type, token, name); + } + + WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); + attrs.setTitle(name); + + final WindowState w = new WindowState(sWm, mMockSession, mIWindow, token, parent, OP_NONE, + 0, attrs, 0, 0); + // TODO: Probably better to make this call in the WindowState ctor to avoid errors with + // adding it to the token... + token.addWindow(w); + return w; + } +} 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 d12d672ab5cc..d6bfa177e440 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -47,17 +47,7 @@ import static org.mockito.Mockito.mock; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class WindowTokenTests { - - private WindowManagerService mWm = null; - private final IWindow mIWindow = new TestIWindow(); - private final Session mMockSession = mock(Session.class); - - @Before - public void setUp() throws Exception { - final Context context = InstrumentationRegistry.getTargetContext(); - mWm = TestWindowManagerPolicy.getWindowManagerService(context); - } +public class WindowTokenTests extends WindowTestsBase { @Test public void testAddWindow() throws Exception { @@ -65,11 +55,11 @@ public class WindowTokenTests { assertEquals(0, token.getWindowsCount()); - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token); - final WindowState window3 = createWindow(null, TYPE_APPLICATION, token); + 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"); token.addWindow(window1); // NOTE: Child windows will not be added to the token as window containers can only @@ -91,12 +81,12 @@ public class WindowTokenTests { @Test public void testChildRemoval() throws Exception { final TestWindowToken token = new TestWindowToken(); - final DisplayContent dc = mWm.getDefaultDisplayContentLocked(); + final DisplayContent dc = sWm.getDefaultDisplayContentLocked(); assertEquals(token, dc.getWindowToken(token.token)); - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token); + final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); + final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); token.addWindow(window1); token.addWindow(window2); @@ -113,11 +103,11 @@ public class WindowTokenTests { @Test public void testAdjustAnimLayer() throws Exception { final TestWindowToken token = new TestWindowToken(); - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token); - final WindowState window3 = createWindow(null, TYPE_APPLICATION, token); + 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"); token.addWindow(window1); token.addWindow(window2); @@ -136,60 +126,11 @@ public class WindowTokenTests { assertEquals(window3StartLayer + adj, highestLayer); } - @Test - public void testGetTopWindow() throws Exception { - final TestWindowToken token = new TestWindowToken(); - - assertNull(token.getTopWindow()); - - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token); - token.addWindow(window1); - assertEquals(window1, token.getTopWindow()); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token); - assertEquals(window12, token.getTopWindow()); - - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token); - token.addWindow(window2); - // Since new windows are added to the bottom of the token, we would still expect the - // previous one to the top. - assertEquals(window12, token.getTopWindow()); - } - - @Test - public void testGetWindowIndex() throws Exception { - final TestWindowToken token = new TestWindowToken(); - - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token); - assertEquals(-1, token.getWindowIndex(window1)); - token.addWindow(window1); - assertEquals(0, token.getWindowIndex(window1)); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token); - // Child windows should report the same index as their parents. - assertEquals(0, token.getWindowIndex(window11)); - assertEquals(0, token.getWindowIndex(window12)); - - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token); - assertEquals(-1, token.getWindowIndex(window2)); - token.addWindow(window2); - // Since new windows are added to the bottom of the token, we would expect the added window - // to be at index 0. - assertEquals(0, token.getWindowIndex(window2)); - assertEquals(1, token.getWindowIndex(window1)); - } - - private WindowState createWindow(WindowState parent, int type, WindowToken token) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); - - return new WindowState(mWm, mMockSession, mIWindow, token, parent, OP_NONE, 0, attrs, 0, 0); - } - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ private class TestWindowToken extends WindowToken { TestWindowToken() { - super(mWm, mock(IBinder.class), 0, false, mWm.getDefaultDisplayContentLocked()); + super(sWm, mock(IBinder.class), 0, false, sWm.getDefaultDisplayContentLocked()); } int getWindowsCount() { |