1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 22 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 23 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; 28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable; 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 36 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 37 38 import android.app.ActivityManagerInternal; 39 import android.app.AppOpsManager; 40 import android.content.BroadcastReceiver; 41 import android.content.ContentResolver; 42 import android.content.Context; 43 import android.content.IntentFilter; 44 import android.database.ContentObserver; 45 import android.hardware.display.DisplayManagerInternal; 46 import android.net.Uri; 47 import android.os.Handler; 48 import android.os.PowerManagerInternal; 49 import android.os.PowerSaveState; 50 import android.os.UserHandle; 51 import android.view.Display; 52 import android.view.InputChannel; 53 54 import com.android.dx.mockito.inline.extended.StaticMockitoSession; 55 import com.android.server.LocalServices; 56 import com.android.server.LockGuard; 57 import com.android.server.Watchdog; 58 import com.android.server.input.InputManagerService; 59 import com.android.server.policy.WindowManagerPolicy; 60 import com.android.server.wm.utils.MockTracker; 61 62 import org.junit.rules.TestRule; 63 import org.junit.runner.Description; 64 import org.junit.runners.model.Statement; 65 import org.mockito.invocation.InvocationOnMock; 66 import org.mockito.quality.Strictness; 67 68 import java.util.concurrent.atomic.AtomicBoolean; 69 70 /** 71 * JUnit test rule to create a mock {@link WindowManagerService} instance for tests. 72 */ 73 public class SystemServicesTestRule implements TestRule { 74 75 private static final String TAG = SystemServicesTestRule.class.getSimpleName(); 76 77 private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false); 78 79 private MockTracker mMockTracker; 80 private StaticMockitoSession mMockitoSession; 81 private WindowManagerService mWindowManagerService; 82 private TestWindowManagerPolicy mWindowManagerPolicy; 83 84 /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */ 85 private static class Tracker extends MockTracker { 86 // This empty extended class is necessary since Mockito distinguishes a listener by it 87 // class. 88 } 89 90 @Override apply(Statement base, Description description)91 public Statement apply(Statement base, Description description) { 92 return new Statement() { 93 @Override 94 public void evaluate() throws Throwable { 95 try { 96 runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp); 97 base.evaluate(); 98 } finally { 99 tearDown(); 100 } 101 } 102 }; 103 } 104 105 private void setUp() { 106 mMockTracker = new Tracker(); 107 108 mMockitoSession = mockitoSession() 109 .spyStatic(LocalServices.class) 110 .mockStatic(LockGuard.class) 111 .mockStatic(Watchdog.class) 112 .strictness(Strictness.LENIENT) 113 .startMocking(); 114 115 doReturn(mock(Watchdog.class)).when(Watchdog::getInstance); 116 117 final Context context = getInstrumentation().getTargetContext(); 118 spyOn(context); 119 120 doReturn(null).when(context) 121 .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class)); 122 doReturn(null).when(context) 123 .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class), 124 any(IntentFilter.class), nullable(String.class), nullable(Handler.class)); 125 126 final ContentResolver contentResolver = context.getContentResolver(); 127 spyOn(contentResolver); 128 doNothing().when(contentResolver) 129 .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class), 130 anyInt()); 131 132 final AppOpsManager appOpsManager = mock(AppOpsManager.class); 133 doReturn(appOpsManager).when(context) 134 .getSystemService(eq(Context.APP_OPS_SERVICE)); 135 136 final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class); 137 doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class))); 138 139 final PowerManagerInternal pmi = mock(PowerManagerInternal.class); 140 final PowerSaveState state = new PowerSaveState.Builder().build(); 141 doReturn(state).when(pmi).getLowPowerState(anyInt()); 142 doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class))); 143 144 final ActivityManagerInternal ami = mock(ActivityManagerInternal.class); 145 doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class))); 146 147 final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class); 148 doAnswer((InvocationOnMock invocationOnMock) -> { 149 final Runnable runnable = invocationOnMock.getArgument(0); 150 if (runnable != null) { 151 runnable.run(); 152 } 153 return null; 154 }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt()); 155 doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class))); 156 157 final InputManagerService ims = mock(InputManagerService.class); 158 // InputChannel is final and can't be mocked. 159 final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); 160 if (input != null && input.length > 1) { 161 doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); 162 } 163 164 final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); 165 final TaskChangeNotificationController taskChangeNotificationController = mock( 166 TaskChangeNotificationController.class); 167 doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController(); 168 final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock(); 169 doReturn(wmLock).when(atms).getGlobalLock(); 170 171 mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService); 172 mWindowManagerService = WindowManagerService.main( 173 context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new); 174 175 mWindowManagerService.onInitReady(); 176 177 final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); 178 // Display creation is driven by the ActivityManagerService via 179 // ActivityStackSupervisor. We emulate those steps here. 180 mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); 181 182 mMockTracker.stopTracking(); 183 } 184 185 private void tearDown() { 186 waitUntilWindowManagerHandlersIdle(); 187 removeLocalServices(); 188 mWindowManagerService = null; 189 mWindowManagerPolicy = null; 190 if (mMockitoSession != null) { 191 mMockitoSession.finishMocking(); 192 mMockitoSession = null; 193 } 194 195 if (mMockTracker != null) { 196 mMockTracker.close(); 197 mMockTracker = null; 198 } 199 } 200 201 private static void removeLocalServices() { 202 LocalServices.removeServiceForTest(WindowManagerInternal.class); 203 LocalServices.removeServiceForTest(WindowManagerPolicy.class); 204 } 205 206 WindowManagerService getWindowManagerService() { 207 return mWindowManagerService; 208 } 209 210 void cleanupWindowManagerHandlers() { 211 final WindowManagerService wm = getWindowManagerService(); 212 if (wm == null) { 213 return; 214 } 215 wm.mH.removeCallbacksAndMessages(null); 216 wm.mAnimationHandler.removeCallbacksAndMessages(null); 217 SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null); 218 } 219 220 void waitUntilWindowManagerHandlersIdle() { 221 final WindowManagerService wm = getWindowManagerService(); 222 if (wm == null) { 223 return; 224 } 225 // Removing delayed FORCE_GC message decreases time for waiting idle. 226 wm.mH.removeMessages(WindowManagerService.H.FORCE_GC); 227 waitHandlerIdle(wm.mH); 228 waitHandlerIdle(wm.mAnimationHandler); 229 waitHandlerIdle(SurfaceAnimationThread.getHandler()); 230 } 231 232 private void waitHandlerIdle(Handler handler) { 233 synchronized (mCurrentMessagesProcessed) { 234 // Add a message to the handler queue and make sure it is fully processed before we move 235 // on. This makes sure all previous messages in the handler are fully processed vs. just 236 // popping them from the message queue. 237 mCurrentMessagesProcessed.set(false); 238 handler.post(() -> { 239 synchronized (mCurrentMessagesProcessed) { 240 mCurrentMessagesProcessed.set(true); 241 mCurrentMessagesProcessed.notifyAll(); 242 } 243 }); 244 while (!mCurrentMessagesProcessed.get()) { 245 try { 246 mCurrentMessagesProcessed.wait(); 247 } catch (InterruptedException e) { 248 } 249 } 250 } 251 } 252 } 253