Merge "Fixed issue with IME displaying on-top of nav bar."

This commit is contained in:
Wale Ogunwale
2016-11-16 21:58:02 +00:00
committed by Android (Google) Code Review
9 changed files with 339 additions and 230 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -175,21 +175,6 @@ class WindowToken extends WindowContainer<WindowState> {
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.

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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() {