From 9bafc7150ea41758cf40ba60eb90deb62217fc34 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 19 Jan 2017 17:28:30 +0100 Subject: [PATCH] Starting window tests, yay! Test: bit FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests Fixes: 34364463 Fixes: 34361417 Change-Id: Ie1b8debc894e5cad8fe517912a1991a38661dfaa --- .../server/wm/AppWindowContainerController.java | 3 +- .../wm/AppWindowContainerControllerTests.java | 65 +++++++++++++++++++++- .../android/server/wm/TestWindowManagerPolicy.java | 33 ++++++++++- .../wm/UnknownAppVisibilityControllerTest.java | 8 +-- .../src/com/android/server/wm/WindowTestsBase.java | 53 +++++++++++++----- 5 files changed, 137 insertions(+), 25 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 0436139f4fda..27e0f292fb65 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -56,7 +56,7 @@ public class AppWindowContainerController private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; private final IApplicationToken mToken; - private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Handler mHandler; private final Runnable mOnWindowsDrawn = () -> { if (mListener == null) { @@ -186,6 +186,7 @@ public class AppWindowContainerController int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, WindowManagerService service) { super(listener, service); + mHandler = new Handler(service.mH.getLooper()); mToken = token; synchronized(mWindowMap) { AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder()); diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 26accc3bcd41..2af4163770ae 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -18,12 +18,10 @@ package com.android.server.wm; import org.junit.Test; -import android.os.Binder; -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.IApplicationToken; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -74,6 +72,67 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { controller.removeContainer(sDisplayContent.getDisplayId()); // Assert orientation is unspecified to after container is removed. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); + + // Reset display frozen state + sWm.mDisplayFrozen = false; + } + + private void assertHasStartingWindow(AppWindowToken atoken) { + assertNotNull(atoken.startingSurface); + assertNotNull(atoken.startingData); + assertNotNull(atoken.startingWindow); + } + + private void assertNoStartingWindow(AppWindowToken atoken) { + assertNull(atoken.startingSurface); + assertNull(atoken.startingWindow); + assertNull(atoken.startingData); + } + + @Test + public void testCreateRemoveStartingWindow() throws Exception { + final TestAppWindowContainerController controller = createAppWindowController(); + controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); + waitUntilHandlerIdle(); + final AppWindowToken atoken = controller.getAppWindowToken(); + assertHasStartingWindow(atoken); + controller.removeStartingWindow(); + waitUntilHandlerIdle(); + assertNoStartingWindow(atoken); + } + + @Test + public void testTransferStartingWindow() throws Exception { + final TestAppWindowContainerController controller1 = createAppWindowController(); + final TestAppWindowContainerController controller2 = createAppWindowController(); + controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); + waitUntilHandlerIdle(); + controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), + true, true, false); + waitUntilHandlerIdle(); + assertNoStartingWindow(controller1.getAppWindowToken()); + assertHasStartingWindow(controller2.getAppWindowToken()); + } + + @Test + public void testTransferStartingWindowWhileCreating() throws Exception { + final TestAppWindowContainerController controller1 = createAppWindowController(); + final TestAppWindowContainerController controller2 = createAppWindowController(); + sPolicy.setRunnableWhenAddingSplashScreen(() -> { + + // Surprise, ...! Transfer window in the middle of the creation flow. + controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), + true, true, false); + }); + controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); + waitUntilHandlerIdle(); + assertNoStartingWindow(controller1.getAppWindowToken()); + assertHasStartingWindow(controller2.getAppWindowToken()); } private TestAppWindowContainerController createAppWindowController() { diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 269b71986e72..ec429a05e3ca 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY; @@ -74,6 +75,7 @@ import android.view.Display; import android.view.IWindowManager; import android.view.KeyEvent; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; import android.view.animation.Animation; import android.os.PowerManagerInternal; @@ -92,6 +94,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { int rotationToReport = 0; + private Runnable mRunnableWhenAddingSplashScreen; + static synchronized WindowManagerService getWindowManagerService(Context context) { if (sWm == null) { // We only want to do this once for the test process as we don't want WM to try to @@ -318,11 +322,36 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return false; } + /** + * Sets a runnable to run when adding a splash screen which gets executed after the window has + * been added but before returning the surface. + */ + void setRunnableWhenAddingSplashScreen(Runnable r) { + mRunnableWhenAddingSplashScreen = r; + } + @Override public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) { - return null; + final com.android.server.wm.WindowState window; + final AppWindowToken atoken; + synchronized (sWm.mWindowMap) { + atoken = WindowTestsBase.sDisplayContent.getAppWindowToken(appToken); + window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken, + "Starting window"); + atoken.startingWindow = window; + } + if (mRunnableWhenAddingSplashScreen != null) { + mRunnableWhenAddingSplashScreen.run(); + mRunnableWhenAddingSplashScreen = null; + } + return () -> { + synchronized (sWm.mWindowMap) { + atoken.removeChild(window); + atoken.startingWindow = null; + } + }; } @Override @@ -482,7 +511,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isScreenOn() { - return false; + return true; } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 612919845f92..772bfb49ed4f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -45,20 +45,18 @@ import org.mockito.invocation.InvocationOnMock; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class UnknownAppVisibilityControllerTest { +public class UnknownAppVisibilityControllerTest extends WindowTestsBase { private WindowManagerService mWm; - private @Mock ActivityManagerInternal mAm; @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); + super.setUp(); final Context context = InstrumentationRegistry.getTargetContext(); - LocalServices.addService(ActivityManagerInternal.class, mAm); doAnswer((InvocationOnMock invocationOnMock) -> { invocationOnMock.getArgumentAt(0, Runnable.class).run(); return null; - }).when(mAm).notifyKeyguardFlagsChanged(any()); + }).when(sMockAm).notifyKeyguardFlagsChanged(any()); mWm = TestWindowManagerPolicy.getWindowManagerService(context); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 813d2638ac94..7c25e43c11aa 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -16,15 +16,17 @@ package com.android.server.wm; +import android.app.ActivityManagerInternal; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.view.IApplicationToken; import org.junit.Assert; import org.junit.Before; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; -import android.app.ActivityManager; -import android.app.ActivityManager.TaskSnapshot; import android.content.Context; import android.os.IBinder; import android.support.test.InstrumentationRegistry; @@ -50,13 +52,17 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.mockito.Mockito.mock; +import com.android.server.AttributeCache; +import com.android.server.LocalServices; + /** * Common base class for window manager unit test classes. */ class WindowTestsBase { static WindowManagerService sWm = null; - private final IWindow mIWindow = new TestIWindow(); - private final Session mMockSession = mock(Session.class); + static TestWindowManagerPolicy sPolicy = null; + private final static IWindow sIWindow = new TestIWindow(); + private final static Session sMockSession = mock(Session.class); static int sNextStackId = FIRST_DYNAMIC_STACK_ID; private static int sNextTaskId = 0; @@ -72,19 +78,27 @@ class WindowTestsBase { static WindowState sAppWindow; static WindowState sChildAppWindowAbove; static WindowState sChildAppWindowBelow; + static @Mock ActivityManagerInternal sMockAm; @Before public void setUp() throws Exception { if (sOneTimeSetupDone) { + Mockito.reset(sMockAm); return; } sOneTimeSetupDone = true; + MockitoAnnotations.initMocks(this); final Context context = InstrumentationRegistry.getTargetContext(); + LocalServices.addService(ActivityManagerInternal.class, sMockAm); + AttributeCache.init(context); sWm = TestWindowManagerPolicy.getWindowManagerService(context); + sPolicy = (TestWindowManagerPolicy) sWm.mPolicy; sLayersController = new WindowLayersController(sWm); sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController, new WallpaperController(sWm)); sWm.mRoot.addChild(sDisplayContent, 0); + sWm.mDisplayEnabled = true; + sWm.mDisplayReady = true; // Set-up some common windows. sWallpaperWindow = createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow"); @@ -108,7 +122,14 @@ class WindowTestsBase { Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second); } - private WindowToken createWindowToken(DisplayContent dc, int type) { + /** + * Waits until the main handler for WM has processed all messages. + */ + void waitUntilHandlerIdle() { + sWm.mH.runWithScissors(() -> { }, 0); + } + + private static WindowToken createWindowToken(DisplayContent dc, int type) { if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { return new TestWindowToken(type, dc); } @@ -120,7 +141,7 @@ class WindowTestsBase { return token; } - WindowState createWindow(WindowState parent, int type, String name) { + static WindowState createWindow(WindowState parent, int type, String name) { return (parent == null) ? createWindow(parent, type, sDisplayContent, name) : createWindow(parent, type, parent.mToken, name); @@ -132,16 +153,16 @@ class WindowTestsBase { return createWindow(null, type, token, name); } - WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { + static 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) { + static 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, + final WindowState w = new WindowState(sWm, sMockSession, sIWindow, 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... @@ -150,14 +171,14 @@ class WindowTestsBase { } /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */ - TaskStack createTaskStackOnDisplay(DisplayContent dc) { + static TaskStack createTaskStackOnDisplay(DisplayContent dc) { final int stackId = sNextStackId++; dc.addStackToDisplay(stackId, true); return sWm.mStackIdToStack.get(stackId); } /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - Task createTaskInStack(TaskStack stack, int userId) { + static Task createTaskInStack(TaskStack stack, int userId) { final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0, false, null); stack.addTask(newTask, POSITION_TOP); @@ -165,7 +186,7 @@ class WindowTestsBase { } /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - class TestWindowToken extends WindowToken { + static class TestWindowToken extends WindowToken { TestWindowToken(int type, DisplayContent dc) { this(type, dc, false /* persistOnEmpty */); @@ -185,7 +206,7 @@ class WindowTestsBase { } /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - class TestAppWindowToken extends AppWindowToken { + static class TestAppWindowToken extends AppWindowToken { TestAppWindowToken(DisplayContent dc) { super(sWm, null, false, dc); @@ -279,6 +300,10 @@ class WindowTestsBase { 0 /* inputDispatchingTimeoutNanos */, sWm); mToken = token; } + + AppWindowToken getAppWindowToken() { + return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); + } } class TestIApplicationToken implements IApplicationToken { @@ -295,7 +320,7 @@ class WindowTestsBase { boolean resizeReported; TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { - super(sWm, mMockSession, mIWindow, token, null, OP_NONE, 0, attrs, 0, 0); + super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0); } @Override -- 2.11.0