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
This commit is contained in:
Tadashi G. Takaoka
2018-12-13 17:09:12 +09:00
parent 809cbc53e0
commit 5a108b83d8
8 changed files with 267 additions and 321 deletions

View File

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

View File

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

View File

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

View File

@@ -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<View> mWindows = new ArrayList<>();

View File

@@ -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.
*
* <p>
* Usage:
* <pre>
* {@literal @}Rule
* public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
* </pre>
*/
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<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
// Record all {@link SurfaceControl} created while testing and releases native resources when
// test finishes.
private final List<WeakReference<SurfaceControl>> 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.<Runnable>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<SurfaceControl> 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<Transaction> reference : mSurfaceTransactions) {
final Transaction transaction = reference.get();
if (transaction != null) {
reference.clear();
transaction.close();
}
}
}
private void destroyAllSurfaceControls() {
for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
final SurfaceControl control = reference.get();
if (control != null) {
reference.clear();
control.destroy();
}
}
}
}

View File

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

View File

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

View File

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