1 /*
2  * Copyright (C) 2017 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 android.app.ActivityManager;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.graphics.Rect;
23 import android.os.Binder;
24 import android.os.IBinder;
25 import android.view.IApplicationToken;
26 import android.view.IWindow;
27 import android.view.WindowManager;
28 
29 import static android.app.AppOpsManager.OP_NONE;
30 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
31 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
32 
33 import static com.android.server.wm.WindowContainer.POSITION_TOP;
34 import static org.mockito.Mockito.any;
35 import static org.mockito.Mockito.anyBoolean;
36 import static org.mockito.Mockito.anyFloat;
37 import static org.mockito.Mockito.anyInt;
38 import static org.mockito.Mockito.doAnswer;
39 import static org.mockito.Mockito.mock;
40 import static org.mockito.Mockito.when;
41 
42 import org.mockito.invocation.InvocationOnMock;
43 
44 /**
45  * A collection of static functions that can be referenced by other test packages to provide access
46  * to WindowManager related test functionality.
47  */
48 public class WindowTestUtils {
49     public static int sNextTaskId = 0;
50 
51     /**
52      * Retrieves an instance of a mock {@link WindowManagerService}.
53      */
getMockWindowManagerService()54     public static WindowManagerService getMockWindowManagerService() {
55         final WindowManagerService service = mock(WindowManagerService.class);
56         final WindowHashMap windowMap = new WindowHashMap();
57         when(service.getWindowManagerLock()).thenReturn(windowMap);
58         return service;
59     }
60 
61     /**
62      * Creates a mock instance of {@link StackWindowController}.
63      */
createMockStackWindowContainerController()64     public static StackWindowController createMockStackWindowContainerController() {
65         StackWindowController controller = mock(StackWindowController.class);
66         controller.mContainer = mock(TestTaskStack.class);
67 
68         // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
69         // to properly set bounds values in the configuration. We must mimick those actions here.
70         doAnswer((InvocationOnMock invocationOnMock) -> {
71             final Configuration config = invocationOnMock.<Configuration>getArgument(7);
72             final Rect bounds = invocationOnMock.<Rect>getArgument(0);
73             config.windowConfiguration.setBounds(bounds);
74             return null;
75         }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
76                 anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
77 
78         return controller;
79     }
80 
81     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
createTaskInStack(WindowManagerService service, TaskStack stack, int userId)82     public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
83             int userId) {
84         synchronized (service.mWindowMap) {
85             final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
86                     new ActivityManager.TaskDescription(), null);
87             stack.addTask(newTask, POSITION_TOP);
88             return newTask;
89         }
90     }
91 
92     /**
93      * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
94      * normally be mocked out.
95      */
96     public static class TestTaskStack extends TaskStack {
TestTaskStack(WindowManagerService service, int stackId)97         TestTaskStack(WindowManagerService service, int stackId) {
98             super(service, stackId, null);
99         }
100 
101         @Override
addTask(Task task, int position, boolean showForAllUsers, boolean moveParents)102         void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
103             // Do nothing.
104         }
105     }
106 
createTestAppWindowToken(DisplayContent dc)107     static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
108         synchronized (dc.mService.mWindowMap) {
109             return new TestAppWindowToken(dc);
110         }
111     }
112 
113     /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
114     public static class TestAppWindowToken extends AppWindowToken {
115         boolean mOnTop = false;
116 
TestAppWindowToken(DisplayContent dc)117         private TestAppWindowToken(DisplayContent dc) {
118             super(dc.mService, new IApplicationToken.Stub() {
119                 public String getName() {return null;}
120                 }, false, dc, true /* fillsParent */);
121         }
122 
TestAppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, AppWindowContainerController controller)123         TestAppWindowToken(WindowManagerService service, IApplicationToken token,
124                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
125                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
126                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
127                 boolean alwaysFocusable, AppWindowContainerController controller) {
128             super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
129                     showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
130                     launchTaskBehind, alwaysFocusable, controller);
131         }
132 
getWindowsCount()133         int getWindowsCount() {
134             return mChildren.size();
135         }
136 
hasWindow(WindowState w)137         boolean hasWindow(WindowState w) {
138             return mChildren.contains(w);
139         }
140 
getFirstChild()141         WindowState getFirstChild() {
142             return mChildren.peekFirst();
143         }
144 
getLastChild()145         WindowState getLastChild() {
146             return mChildren.peekLast();
147         }
148 
positionInParent()149         int positionInParent() {
150             return getParent().mChildren.indexOf(this);
151         }
152 
setIsOnTop(boolean onTop)153         void setIsOnTop(boolean onTop) {
154             mOnTop = onTop;
155         }
156 
157         @Override
isOnTop()158         boolean isOnTop() {
159             return mOnTop;
160         }
161     }
162 
createTestWindowToken(int type, DisplayContent dc)163     static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
164         return createTestWindowToken(type, dc, false /* persistOnEmpty */);
165     }
166 
createTestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty)167     static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
168             boolean persistOnEmpty) {
169         synchronized (dc.mService.mWindowMap) {
170             return new TestWindowToken(type, dc, persistOnEmpty);
171         }
172     }
173 
174     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
175     public static class TestWindowToken extends WindowToken {
176 
TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty)177         private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
178             super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc,
179                     false /* ownerCanManageAppTokens */);
180         }
181 
getWindowsCount()182         int getWindowsCount() {
183             return mChildren.size();
184         }
185 
hasWindow(WindowState w)186         boolean hasWindow(WindowState w) {
187             return mChildren.contains(w);
188         }
189     }
190 
191     /* Used so we can gain access to some protected members of the {@link Task} class */
192     public static class TestTask extends Task {
193         boolean mShouldDeferRemoval = false;
194         boolean mOnDisplayChangedCalled = false;
195         private boolean mIsAnimating = false;
196 
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, boolean supportsPictureInPicture, TaskWindowContainerController controller)197         TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
198                 int resizeMode, boolean supportsPictureInPicture,
199                 TaskWindowContainerController controller) {
200             super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
201                     new ActivityManager.TaskDescription(), controller);
202         }
203 
shouldDeferRemoval()204         boolean shouldDeferRemoval() {
205             return mShouldDeferRemoval;
206         }
207 
positionInParent()208         int positionInParent() {
209             return getParent().mChildren.indexOf(this);
210         }
211 
212         @Override
onDisplayChanged(DisplayContent dc)213         void onDisplayChanged(DisplayContent dc) {
214             super.onDisplayChanged(dc);
215             mOnDisplayChangedCalled = true;
216         }
217 
218         @Override
isSelfAnimating()219         boolean isSelfAnimating() {
220             return mIsAnimating;
221         }
222 
setLocalIsAnimating(boolean isAnimating)223         void setLocalIsAnimating(boolean isAnimating) {
224             mIsAnimating = isAnimating;
225         }
226     }
227 
228     /**
229      * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
230      * class.
231      */
232     public static class TestTaskWindowContainerController extends TaskWindowContainerController {
233 
TestTaskWindowContainerController(WindowTestsBase testsBase)234         TestTaskWindowContainerController(WindowTestsBase testsBase) {
235             this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
236         }
237 
TestTaskWindowContainerController(StackWindowController stackController)238         TestTaskWindowContainerController(StackWindowController stackController) {
239             super(sNextTaskId++, new TaskWindowContainerListener() {
240                         @Override
241                         public void registerConfigurationChangeListener(
242                                 ConfigurationContainerListener listener) {
243 
244                         }
245 
246                         @Override
247                         public void unregisterConfigurationChangeListener(
248                                 ConfigurationContainerListener listener) {
249 
250                         }
251 
252                         @Override
253                         public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
254 
255                         }
256 
257                         @Override
258                         public void requestResize(Rect bounds, int resizeMode) {
259 
260                         }
261                     }, stackController, 0 /* userId */, null /* bounds */, RESIZE_MODE_UNRESIZEABLE,
262                     false /* supportsPictureInPicture */, true /* toTop*/,
263                     true /* showForAllUsers */, new ActivityManager.TaskDescription(),
264                     stackController.mService);
265         }
266 
267         @Override
createTask(int taskId, TaskStack stack, int userId, int resizeMode, boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription)268         TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
269                 boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
270             return new TestTask(taskId, stack, userId, mService, resizeMode,
271                     supportsPictureInPicture, this);
272         }
273     }
274 
275     public static class TestAppWindowContainerController extends AppWindowContainerController {
276 
277         final IApplicationToken mToken;
278 
TestAppWindowContainerController(TestTaskWindowContainerController taskController)279         TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
280             this(taskController, new TestIApplicationToken());
281         }
282 
TestAppWindowContainerController(TestTaskWindowContainerController taskController, IApplicationToken token)283         TestAppWindowContainerController(TestTaskWindowContainerController taskController,
284                 IApplicationToken token) {
285             super(taskController, token, null /* listener */, 0 /* index */,
286                     SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */,
287                     true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
288                     false /* launchTaskBehind */, false /* alwaysFocusable */,
289                     0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
290                     0 /* inputDispatchingTimeoutNanos */, taskController.mService);
291             mToken = token;
292         }
293 
294         @Override
createAppWindow(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, AppWindowContainerController controller)295         AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
296                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
297                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
298                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
299                 boolean alwaysFocusable, AppWindowContainerController controller) {
300             return new TestAppWindowToken(service, token, voiceInteraction, dc,
301                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
302                     orientation,
303                     rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
304                     controller);
305         }
306 
getAppWindowToken(DisplayContent dc)307         AppWindowToken getAppWindowToken(DisplayContent dc) {
308             return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
309         }
310     }
311 
312     public static class TestIApplicationToken implements IApplicationToken {
313 
314         private final Binder mBinder = new Binder();
315         @Override
asBinder()316         public IBinder asBinder() {
317             return mBinder;
318         }
319         @Override
getName()320         public String getName() {
321             return null;
322         }
323     }
324 
325     /** Used to track resize reports. */
326     public static class TestWindowState extends WindowState {
327         boolean resizeReported;
328 
TestWindowState(WindowManagerService service, Session session, IWindow window, WindowManager.LayoutParams attrs, WindowToken token)329         TestWindowState(WindowManagerService service, Session session, IWindow window,
330                 WindowManager.LayoutParams attrs, WindowToken token) {
331             super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
332                     false /* ownerCanAddInternalSystemWindow */);
333         }
334 
335         @Override
reportResized()336         void reportResized() {
337             super.reportResized();
338             resizeReported = true;
339         }
340 
341         @Override
isGoneForLayoutLw()342         public boolean isGoneForLayoutLw() {
343             return false;
344         }
345 
346         @Override
updateResizingWindowIfNeeded()347         void updateResizingWindowIfNeeded() {
348             // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
349             // the system that it can actually update the window.
350             boolean hadSurface = mHasSurface;
351             mHasSurface = true;
352 
353             super.updateResizingWindowIfNeeded();
354 
355             mHasSurface = hadSurface;
356         }
357     }
358 }
359