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