Merge "Use singleton WindowManagerService for testing"

This commit is contained in:
TreeHugger Robot
2019-01-08 06:47:55 +00:00
committed by Android (Google) Code Review
8 changed files with 267 additions and 321 deletions

View File

@@ -119,6 +119,10 @@ class ActivityTestsBase {
@After
public void tearDownBase() {
mTestInjector.tearDown();
if (mService != null) {
mService.setWindowManager(null);
mService = null;
}
}
ActivityTaskManagerService createActivityTaskManagerService() {
@@ -638,7 +642,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);
@@ -650,6 +660,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) {
}
}
}