From 5a108b83d8a3f2d2691347fdecbcece6ea62614e Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 13 Dec 2018 17:09:12 +0900 Subject: [PATCH] Use singleton WindowManagerService for testing This CL introduces a single WindowManagerService instance and uses it for all tests that extend WindowTestBase. This way we can decrease the number of object counts in the heap after running 526 presubmit tests in WmTests. Bug: 113239988 Test: Pass all presumit tests in WmTests. $ atest --test-mapping frameworks/base/services/core/java/com/android/server/wm Change-Id: I8e5af477def6207bcc86cace6b240873516d075d --- .../android/server/wm/ActivityTestsBase.java | 11 + .../server/wm/DisplayRotationTests.java | 17 +- .../server/wm/LockTaskControllerTest.java | 45 ++-- .../server/wm/ScreenDecorWindowTests.java | 7 +- .../server/wm/WindowManagerServiceRule.java | 242 ------------------ .../wm/WindowManagerServiceRuleTest.java | 44 ---- .../android/server/wm/WindowTestsBase.java | 18 +- .../com/android/server/wm/WmServiceUtils.java | 204 +++++++++++++++ 8 files changed, 267 insertions(+), 321 deletions(-) delete mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java delete mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 569c6d4af37d3..e3ce6be3e4437 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -115,6 +115,10 @@ class ActivityTestsBase { @After public void tearDownBase() { mTestInjector.tearDown(); + if (mService != null) { + mService.setWindowManager(null); + mService = null; + } } ActivityTaskManagerService createActivityTaskManagerService() { @@ -619,7 +623,13 @@ class ActivityTestsBase { } } + private static WindowManagerService sMockWindowManagerService; + private static WindowManagerService prepareMockWindowManager() { + if (sMockWindowManagerService != null) { + return sMockWindowManagerService; + } + final WindowManagerService service = mock(WindowManagerService.class); service.mRoot = mock(RootWindowContainer.class); @@ -631,6 +641,7 @@ class ActivityTestsBase { return null; }).when(service).inSurfaceTransaction(any()); + sMockWindowManagerService = service; return service; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 6b31e6fdbd289..198e7ce63f527 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -63,6 +63,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -86,7 +87,7 @@ public class DisplayRotationTests { private StatusBarManagerInternal mPreviousStatusBarManagerInternal; - private WindowManagerService mMockWm; + private static WindowManagerService sMockWm; private DisplayContent mMockDisplayContent; private DisplayPolicy mMockDisplayPolicy; private Context mMockContext; @@ -108,13 +109,16 @@ public class DisplayRotationTests { private DisplayRotation mTarget; + @BeforeClass + public static void setUpOnce() { + sMockWm = mock(WindowManagerService.class); + sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + } + @Before public void setUp() { FakeSettingsProvider.clearSettingsProvider(); - mMockWm = mock(WindowManagerService.class); - mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); - mPreviousStatusBarManagerInternal = LocalServices.getService( StatusBarManagerInternal.class); LocalServices.removeServiceForTest(StatusBarManagerInternal.class); @@ -452,7 +456,7 @@ public class DisplayRotationTests { mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); assertTrue(waitForUiHandler()); - verify(mMockWm).updateRotation(false, false); + verify(sMockWm).updateRotation(false, false); } @Test @@ -833,8 +837,9 @@ public class DisplayRotationTests { .thenReturn(mFakeSettingsProvider.getIContentProvider()); mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); - mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy, + mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()); + reset(sMockWm); captureObservers(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a9f150b10e73a..e24eb75a2750b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -34,7 +34,12 @@ import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -47,10 +52,6 @@ import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; @@ -115,6 +116,7 @@ public class LockTaskControllerTest { private LockTaskController mLockTaskController; private Context mContext; + private String mPackageName; private String mLockToAppSetting; @Before @@ -122,6 +124,7 @@ public class LockTaskControllerTest { MockitoAnnotations.initMocks(this); mContext = getInstrumentation().getTargetContext(); + mPackageName = mContext.getPackageName(); mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED); @@ -146,6 +149,7 @@ public class LockTaskControllerTest { @After public void tearDown() throws Exception { + mLockTaskController.setWindowManager(null); Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting); } @@ -223,7 +227,7 @@ public class LockTaskControllerTest { // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE); // THEN screen pinning toast should be shown - verify(mStatusBarService).showPinningEnterExitToast(true /* entering */); + verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */)); } @Test @@ -390,9 +394,9 @@ public class LockTaskControllerTest { // THEN lock task mode should have been finished verifyLockTaskStopped(times(1)); // THEN the keyguard should be shown - verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL); + verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL)); // THEN screen pinning toast should be shown - verify(mStatusBarService).showPinningEnterExitToast(false /* entering */); + verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */)); } @Test @@ -509,9 +513,9 @@ public class LockTaskControllerTest { & ~DISABLE_HOME; int expectedFlags2 = DISABLE2_MASK; verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); // reset invocation counter reset(mStatusBarService); @@ -526,9 +530,9 @@ public class LockTaskControllerTest { expectedFlags2 = DISABLE2_MASK & ~DISABLE2_NOTIFICATION_SHADE; verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); } @Test @@ -548,9 +552,9 @@ public class LockTaskControllerTest { // THEN status bar shouldn't change verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); } @Test @@ -657,14 +661,14 @@ public class LockTaskControllerTest { verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); // THEN recents should have been notified verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID)); // THEN the DO/PO should be informed about the operation - verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME, - TEST_USER_ID); + verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME), + eq(TEST_USER_ID)); } private void verifyLockTaskStopped(VerificationMode mode) throws Exception { @@ -672,11 +676,12 @@ public class LockTaskControllerTest { verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE), - any(IBinder.class), eq(mContext.getPackageName())); + any(IBinder.class), eq(mPackageName)); verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE), - any(IBinder.class), eq(mContext.getPackageName())); + any(IBinder.class), eq(mPackageName)); // THEN the DO/PO should be informed about the operation - verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID); + verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(), + eq(TEST_USER_ID)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index 36eccd1892a7c..03aba39517ebc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -33,6 +33,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -58,7 +60,6 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.widget.TextView; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After; @@ -80,8 +81,8 @@ import java.util.function.BooleanSupplier; @Presubmit public class ScreenDecorWindowTests { - private final Context mContext = InstrumentationRegistry.getTargetContext(); - private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + private final Context mContext = getInstrumentation().getTargetContext(); + private final Instrumentation mInstrumentation = getInstrumentation(); private WindowManager mWm; private ArrayList mWindows = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java deleted file mode 100644 index cba958682401c..0000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2018 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.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; -import static android.view.Display.DEFAULT_DISPLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -import android.app.ActivityManagerInternal; -import android.content.Context; -import android.hardware.display.DisplayManagerInternal; -import android.os.Handler; -import android.os.PowerManagerInternal; -import android.os.PowerSaveState; -import android.view.Display; -import android.view.InputChannel; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; - -import com.android.server.LocalServices; -import com.android.server.input.InputManagerService; -import com.android.server.policy.WindowManagerPolicy; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.invocation.InvocationOnMock; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -/** - * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure - * to properly tear it down after. - * - *

- * Usage: - *

- * {@literal @}Rule
- *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * 
- */ -public class WindowManagerServiceRule implements TestRule { - - private WindowManagerService mService; - private TestWindowManagerPolicy mPolicy; - // Record all {@link SurfaceControl.Transaction} created while testing and releases native - // resources when test finishes. - private final List> mSurfaceTransactions = new ArrayList<>(); - // Record all {@link SurfaceControl} created while testing and releases native resources when - // test finishes. - private final List> mSurfaceControls = new ArrayList<>(); - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - runWithDexmakerShareClassLoader(this::setUp); - try { - base.evaluate(); - } finally { - tearDown(); - } - } - - private void setUp() { - final Context context = getInstrumentation().getTargetContext(); - - removeServices(); - - LocalServices.addService(DisplayManagerInternal.class, - mock(DisplayManagerInternal.class)); - - LocalServices.addService(PowerManagerInternal.class, - mock(PowerManagerInternal.class)); - final PowerManagerInternal pm = - LocalServices.getService(PowerManagerInternal.class); - doNothing().when(pm).registerLowPowerModeObserver(any()); - PowerSaveState state = new PowerSaveState.Builder().build(); - doReturn(state).when(pm).getLowPowerState(anyInt()); - - LocalServices.addService(ActivityManagerInternal.class, - mock(ActivityManagerInternal.class)); - LocalServices.addService(ActivityTaskManagerInternal.class, - mock(ActivityTaskManagerInternal.class)); - final ActivityTaskManagerInternal atm = - LocalServices.getService(ActivityTaskManagerInternal.class); - doAnswer((InvocationOnMock invocationOnMock) -> { - final Runnable runnable = invocationOnMock.getArgument(0); - if (runnable != null) { - runnable.run(); - } - return null; - }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt()); - - InputManagerService ims = mock(InputManagerService.class); - // InputChannel is final and can't be mocked. - InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); - if (input != null && input.length > 1) { - doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); - } - ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); - when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock()); - - mService = WindowManagerService.main(context, ims, false, false, - mPolicy = new TestWindowManagerPolicy( - WindowManagerServiceRule.this::getWindowManagerService), atms); - mService.mTransactionFactory = () -> { - final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - mSurfaceTransactions.add(new WeakReference<>(transaction)); - return transaction; - }; - mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) { - @Override - public SurfaceControl build() { - final SurfaceControl control = super.build(); - mSurfaceControls.add(new WeakReference<>(control)); - return control; - } - }; - - mService.onInitReady(); - - final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); - // Display creation is driven by the ActivityManagerService via - // ActivityStackSupervisor. We emulate those steps here. - mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); - } - - private void removeServices() { - LocalServices.removeServiceForTest(DisplayManagerInternal.class); - LocalServices.removeServiceForTest(PowerManagerInternal.class); - LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerPolicy.class); - } - - private void tearDown() { - cancelAllPendingAnimations(); - waitUntilWindowManagerHandlersIdle(); - destroyAllSurfaceTransactions(); - destroyAllSurfaceControls(); - removeServices(); - mService = null; - mPolicy = null; - } - }; - } - - WindowManagerService getWindowManagerService() { - return mService; - } - - private void cancelAllPendingAnimations() { - for (final WeakReference reference : mSurfaceControls) { - final SurfaceControl sc = reference.get(); - if (sc != null) { - mService.mSurfaceAnimationRunner.onAnimationCancelled(sc); - } - } - } - - void waitUntilWindowManagerHandlersIdle() { - final WindowManagerService wm = getWindowManagerService(); - if (wm == null) { - return; - } - // Removing delayed FORCE_GC message decreases time for waiting idle. - wm.mH.removeMessages(WindowManagerService.H.FORCE_GC); - waitHandlerIdle(wm.mH); - waitHandlerIdle(wm.mAnimationHandler); - waitHandlerIdle(SurfaceAnimationThread.getHandler()); - } - - private static void waitHandlerIdle(Handler handler) { - if (handler.hasMessagesOrCallbacks()) { - final CountDownLatch latch = new CountDownLatch(1); - // Wait for delayed messages are processed. - handler.getLooper().getQueue().addIdleHandler(() -> { - if (handler.hasMessagesOrCallbacks()) { - return true; // keep idle handler. - } - latch.countDown(); - return false; // remove idle handler. - }); - try { - latch.await(); - } catch (InterruptedException e) { - } - } - } - - private void destroyAllSurfaceTransactions() { - for (final WeakReference reference : mSurfaceTransactions) { - final Transaction transaction = reference.get(); - if (transaction != null) { - reference.clear(); - transaction.close(); - } - } - } - - private void destroyAllSurfaceControls() { - for (final WeakReference reference : mSurfaceControls) { - final SurfaceControl control = reference.get(); - if (control != null) { - reference.clear(); - control.destroy(); - } - } - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java deleted file mode 100644 index 343d35959df4f..0000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 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 org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Rule; -import org.junit.Test; - -/** - * Build/InstallRun: - * atest FrameworksServicesTests:WindowManagerServiceRuleTest - */ -@Presubmit -@SmallTest -public class WindowManagerServiceRuleTest { - - @Rule - public final WindowManagerServiceRule mRule = new WindowManagerServiceRule(); - - @Test - public void testWindowManagerSetUp() { - assertThat(mRule.getWindowManagerService(), notNullValue()); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 5c3368bd25acc..3813891ef0e0a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -53,6 +53,7 @@ import android.view.WindowManager; import com.android.server.AttributeCache; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -95,17 +96,22 @@ class WindowTestsBase { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); - @Rule - public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule(); - - static WindowState.PowerManagerWrapper sPowerManagerWrapper; // TODO(roosa): make non-static. + static WindowState.PowerManagerWrapper sPowerManagerWrapper; @BeforeClass public static void setUpOnceBase() { AttributeCache.init(getInstrumentation().getTargetContext()); + + WmServiceUtils.setUpWindowManagerService(); + sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); } + @AfterClass + public static void tearDonwOnceBase() { + WmServiceUtils.tearDownWindowManagerService(); + } + @Before public void setUpBase() { // If @Before throws an exception, the error isn't logged. This will make sure any failures @@ -115,7 +121,7 @@ class WindowTestsBase { final Context context = getInstrumentation().getTargetContext(); - mWm = mWmRule.getWindowManagerService(); + mWm = WmServiceUtils.getWindowManagerService(); beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); @@ -214,7 +220,7 @@ class WindowTestsBase { * Waits until the main handler for WM has processed all messages. */ void waitUntilHandlersIdle() { - mWmRule.waitUntilWindowManagerHandlersIdle(); + WmServiceUtils.waitUntilWindowManagerHandlersIdle(); } private WindowToken createWindowToken( diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java new file mode 100644 index 0000000000000..2465e5d89bb11 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019 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.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.hardware.display.DisplayManagerInternal; +import android.net.Uri; +import android.os.Handler; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.os.UserHandle; +import android.view.Display; +import android.view.InputChannel; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.LocalServices; +import com.android.server.LockGuard; +import com.android.server.Watchdog; +import com.android.server.input.InputManagerService; +import com.android.server.policy.WindowManagerPolicy; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; + +import java.util.concurrent.CountDownLatch; + +/** + * A Test utility class to create a mock {@link WindowManagerService} instance for tests. + */ +class WmServiceUtils { + private static StaticMockitoSession sMockitoSession; + private static WindowManagerService sService; + private static TestWindowManagerPolicy sPolicy; + + static void setUpWindowManagerService() { + sMockitoSession = mockitoSession() + .spyStatic(LockGuard.class) + .spyStatic(Watchdog.class) + .strictness(Strictness.LENIENT) + .startMocking(); + + runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService); + } + + static void tearDownWindowManagerService() { + waitUntilWindowManagerHandlersIdle(); + removeLocalServices(); + sService = null; + sPolicy = null; + + sMockitoSession.finishMocking(); + } + + private static void setUpTestWindowService() { + doReturn(null).when(() -> LockGuard.installLock(any(), anyInt())); + doReturn(mock(Watchdog.class)).when(Watchdog::getInstance); + + final Context context = getInstrumentation().getTargetContext(); + spyOn(context); + + doReturn(null).when(context) + .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class)); + doReturn(null).when(context) + .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class), + any(IntentFilter.class), nullable(String.class), nullable(Handler.class)); + + final ContentResolver contentResolver = context.getContentResolver(); + spyOn(contentResolver); + doNothing().when(contentResolver) + .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class), + anyInt()); + + final AppOpsManager appOpsManager = mock(AppOpsManager.class); + doReturn(appOpsManager).when(context) + .getSystemService(eq(Context.APP_OPS_SERVICE)); + + removeLocalServices(); + + final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, dmi); + + final PowerManagerInternal pmi = mock(PowerManagerInternal.class); + LocalServices.addService(PowerManagerInternal.class, pmi); + final PowerSaveState state = new PowerSaveState.Builder().build(); + doReturn(state).when(pmi).getLowPowerState(anyInt()); + + final ActivityManagerInternal ami = mock(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, ami); + + final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class); + LocalServices.addService(ActivityTaskManagerInternal.class, atmi); + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable runnable = invocationOnMock.getArgument(0); + if (runnable != null) { + runnable.run(); + } + return null; + }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt()); + + final InputManagerService ims = mock(InputManagerService.class); + // InputChannel is final and can't be mocked. + final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); + if (input != null && input.length > 1) { + doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); + } + + final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); + final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock(); + doReturn(wmLock).when(atms).getGlobalLock(); + + sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService); + sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms); + + sService.onInitReady(); + + final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); + // Display creation is driven by the ActivityManagerService via + // ActivityStackSupervisor. We emulate those steps here. + sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + } + + private static void removeLocalServices() { + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + } + + static WindowManagerService getWindowManagerService() { + return sService; + } + + static void waitUntilWindowManagerHandlersIdle() { + final WindowManagerService wm = getWindowManagerService(); + if (wm == null) { + return; + } + // Removing delayed FORCE_GC message decreases time for waiting idle. + wm.mH.removeMessages(WindowManagerService.H.FORCE_GC); + waitHandlerIdle(wm.mH); + waitHandlerIdle(wm.mAnimationHandler); + waitHandlerIdle(SurfaceAnimationThread.getHandler()); + } + + private static void waitHandlerIdle(Handler handler) { + if (!handler.hasMessagesOrCallbacks()) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + // Wait for delayed messages are processed. + handler.getLooper().getQueue().addIdleHandler(() -> { + if (handler.hasMessagesOrCallbacks()) { + return true; // keep idle handler. + } + latch.countDown(); + return false; // remove idle handler. + }); + try { + latch.await(); + } catch (InterruptedException e) { + } + } +}