From 44fbdf5b1e13398e35d4bafb7236d194a51ee7af Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Wed, 16 Nov 2016 10:18:45 -0800 Subject: [PATCH] Fixed issue with IME displaying on-top of nav bar. Caused by some recent refactoring. We now make sure the IME has the higher animation layer in its base layer of the window it is targeting. Also, consolidated some of our test functions. Bug: 32916670 Test: bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests Change-Id: I0b1abd6fead981cfc810488cc785261abba5341d --- .../server/wm/WindowLayersController.java | 47 ++++-- .../com/android/server/wm/WindowState.java | 4 +- .../com/android/server/wm/WindowToken.java | 15 -- .../server/wm/AppWindowTokenTests.java | 34 ++-- .../server/wm/DisplayContentTests.java | 62 +------- .../wm/WindowLayersControllerTests.java | 149 ++++++++++++++++++ .../android/server/wm/WindowStateTests.java | 79 +++++----- .../android/server/wm/WindowTestsBase.java | 90 +++++++++++ .../android/server/wm/WindowTokenTests.java | 89 ++--------- 9 files changed, 339 insertions(+), 230 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java create mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java index c06e5cc6d7ad1..32373f901be32 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 c11003dd972b9..661df9cd60ada 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 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 402bcfb863f0b..a2eebc336970a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -175,21 +175,6 @@ class WindowToken extends WindowContainer { return (WindowState) mChildren.get(mChildren.size() - 1).getTop(); } - /** - * 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 772b2a2c4737e..207939fc9dd94 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 225dc5d2d66ae..0801a883177d2 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 0000000000000..5a035d636443c --- /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 4499275e8f468..50e5a2220ee3a 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 0000000000000..9681bd2f5947e --- /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 d12d672ab5cce..d6bfa177e4406 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() {