blob: 7918901f7e2db216726a34fa59c38c1d482c1152 [file] [log] [blame]
/*
* 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.View.VISIBLE;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.view.Display;
import android.view.DisplayInfo;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.view.IWindow;
import android.view.WindowManager;
import static android.app.AppOpsManager.OP_NONE;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
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_OVERLAY;
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_WALLPAPER;
import static org.mockito.Mockito.mock;
import com.android.server.AttributeCache;
import java.util.HashSet;
import java.util.LinkedList;
/**
* Common base class for window manager unit test classes.
*/
class WindowTestsBase {
static WindowManagerService sWm = null;
private static final IWindow sIWindow = new TestIWindow();
private static Session sMockSession;
// The default display is removed in {@link #setUp} and then we iterate over all displays to
// make sure we don't collide with any existing display. If we run into no other display, the
// added display should be treated as default. This cannot be the default display
private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
static int sNextStackId = 1000;
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
WindowState mStatusBarWindow;
WindowState mDockedDividerWindow;
WindowState mNavBarWindow;
WindowState mAppWindow;
WindowState mChildAppWindowAbove;
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
private final SurfaceAnimationRunner mSurfaceAnimationRunner;
public WindowTestsBase() {
this(new SurfaceAnimationRunner());
}
public WindowTestsBase(SurfaceAnimationRunner surfaceAnimationRunner) {
mSurfaceAnimationRunner = surfaceAnimationRunner;
}
@Before
public void setUp() throws Exception {
if (!sOneTimeSetupDone) {
sOneTimeSetupDone = true;
// Allows to mock package local classes and methods
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
sMockSession = mock(Session.class);
}
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
sWm = TestWindowManagerPolicy.getWindowManagerService(context, mSurfaceAnimationRunner);
beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
sWm.mDisplayEnabled = true;
sWm.mDisplayReady = true;
// Set-up some common windows.
mCommonWindows = new HashSet();
mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
sWm.mInputMethodWindow = mImeWindow;
mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, "mImeDialogWindow");
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, "mDockedDividerWindow");
mAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
mChildAppWindowAbove = createCommonWindow(mAppWindow, TYPE_APPLICATION_ATTACHED_DIALOG,
"mChildAppWindowAbove");
mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
// Adding a display will cause freezing the display. Make sure to wait until it's unfrozen
// to not run into race conditions with the tests.
waitUntilHandlersIdle();
}
void beforeCreateDisplay() {
// Called before display is created.
}
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
synchronized (sWm.mWindowMap) {
sWm.mRoot.forAllWindows(w -> {
if (!mCommonWindows.contains(w)) {
nonCommonWindows.addLast(w);
}
}, true /* traverseTopToBottom */);
while (!nonCommonWindows.isEmpty()) {
nonCommonWindows.pollLast().removeImmediately();
}
mDisplayContent.removeImmediately();
sWm.mInputMethodTarget = null;
}
// Wait until everything is really cleaned up.
waitUntilHandlersIdle();
}
/**
* @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
* set-up (or null).
*/
SurfaceBuilderFactory getSurfaceBuilderFactory() {
return null;
}
private WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
mCommonWindows.add(win);
// Prevent common windows from been IMe targets
win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
return win;
}
/** 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);
}
/** Asserts that the first entry is greater than the second entry. */
void assertLessThan(int first, int second) throws Exception {
Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
}
/**
* Waits until the main handler for WM has processed all messages.
*/
void waitUntilHandlersIdle() {
sWm.mH.runWithScissors(() -> { }, 0);
sWm.mAnimationHandler.runWithScissors(() -> { }, 0);
SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
}
private WindowToken createWindowToken(
DisplayContent dc, int windowingMode, int activityType, int type) {
if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
return new WindowTestUtils.TestWindowToken(type, dc);
}
final TaskStack stack =
createStackControllerOnStackOnDisplay(windowingMode, activityType, dc).mContainer;
final Task task = createTaskInStack(stack, 0 /* userId */);
final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc);
task.addChild(token, 0);
return token;
}
WindowState createWindow(WindowState parent, int type, String name) {
return (parent == null)
? createWindow(parent, type, mDisplayContent, name)
: createWindow(parent, type, parent.mToken, name);
}
WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
int type, DisplayContent dc, String name) {
final WindowToken token = createWindowToken(dc, windowingMode, activityType, type);
return createWindow(parent, type, token, name);
}
WindowState createAppWindow(Task task, int type, String name) {
final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
task.addChild(token, 0);
return createWindow(null, type, token, name);
}
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
return createWindow(parent, type, token, name);
}
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
boolean ownerCanAddInternalSystemWindow) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
return createWindow(parent, type, token, name, 0 /* ownerId */,
ownerCanAddInternalSystemWindow);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
return createWindow(parent, type, token, name, 0 /* ownerId */,
false /* ownerCanAddInternalSystemWindow */);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow);
// 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;
}
/** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
TaskStack createTaskStackOnDisplay(DisplayContent dc) {
return createStackControllerOnDisplay(dc).mContainer;
}
StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
return createStackControllerOnStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
}
StackWindowController createStackControllerOnStackOnDisplay(
int windowingMode, int activityType, DisplayContent dc) {
final Configuration overrideConfig = new Configuration();
overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
overrideConfig.windowConfiguration.setActivityType(activityType);
final int stackId = ++sNextStackId;
final StackWindowController controller = new StackWindowController(stackId, null,
dc.getDisplayId(), true /* onTop */, new Rect(), sWm);
controller.onOverrideConfigurationChanged(overrideConfig);
return controller;
}
/** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
Task createTaskInStack(TaskStack stack, int userId) {
return WindowTestUtils.createTaskInStack(sWm, stack, userId);
}
/** Creates a {@link DisplayContent} and adds it to the system. */
DisplayContent createNewDisplay() {
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
return new DisplayContent(display, sWm, new WallpaperController(sWm));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs,
WindowToken token) {
return new WindowTestUtils.TestWindowState(sWm, sMockSession, sIWindow, attrs, token);
}
}