WindowTestsBase.java revision 367ff7fd5251790ad8dc086bd386be8cba1dda5c
1/*
2 * Copyright (C) 2016 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
17package com.android.server.wm;
18
19import android.app.ActivityManager.TaskDescription;
20import android.app.ActivityManagerInternal;
21import android.content.res.Configuration;
22import android.graphics.Rect;
23import android.hardware.display.DisplayManagerGlobal;
24import android.os.Binder;
25import android.view.Display;
26import android.view.DisplayInfo;
27import android.view.IApplicationToken;
28import org.junit.Assert;
29import org.junit.Before;
30import org.mockito.Mock;
31import org.mockito.Mockito;
32import org.mockito.MockitoAnnotations;
33
34import android.app.ActivityManager.TaskSnapshot;
35import android.content.Context;
36import android.os.IBinder;
37import android.support.test.InstrumentationRegistry;
38import android.view.IWindow;
39import android.view.WindowManager;
40
41import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
42import static android.app.AppOpsManager.OP_NONE;
43import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
44import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
45import static android.content.res.Configuration.EMPTY;
46import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
47import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
48import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
49import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
50import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
51import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
52import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
53import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
54import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
55import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
56import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
57import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
58import static com.android.server.wm.WindowContainer.POSITION_TOP;
59import static org.mockito.Mockito.mock;
60
61import com.android.server.AttributeCache;
62import com.android.server.LocalServices;
63
64/**
65 * Common base class for window manager unit test classes.
66 */
67class WindowTestsBase {
68    static WindowManagerService sWm = null;
69    static TestWindowManagerPolicy sPolicy = null;
70    private final static IWindow sIWindow = new TestIWindow();
71    private final static Session sMockSession = mock(Session.class);
72    private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1;
73    static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
74    private static int sNextTaskId = 0;
75
76    private static boolean sOneTimeSetupDone = false;
77    static DisplayContent sDisplayContent;
78    static DisplayInfo sDisplayInfo = new DisplayInfo();
79    static WindowLayersController sLayersController;
80    static WindowState sWallpaperWindow;
81    static WindowState sImeWindow;
82    static WindowState sImeDialogWindow;
83    static WindowState sStatusBarWindow;
84    static WindowState sDockedDividerWindow;
85    static WindowState sNavBarWindow;
86    static WindowState sAppWindow;
87    static WindowState sChildAppWindowAbove;
88    static WindowState sChildAppWindowBelow;
89    static @Mock ActivityManagerInternal sMockAm;
90
91    @Before
92    public void setUp() throws Exception {
93        if (sOneTimeSetupDone) {
94            Mockito.reset(sMockAm);
95            return;
96        }
97        sOneTimeSetupDone = true;
98        MockitoAnnotations.initMocks(this);
99        final Context context = InstrumentationRegistry.getTargetContext();
100        LocalServices.addService(ActivityManagerInternal.class, sMockAm);
101        AttributeCache.init(context);
102        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
103        sPolicy = (TestWindowManagerPolicy) sWm.mPolicy;
104        sLayersController = new WindowLayersController(sWm);
105        sDisplayContent = sWm.mRoot.getDisplayContent(context.getDisplay().getDisplayId());
106        if (sDisplayContent != null) {
107            sDisplayContent.removeImmediately();
108        }
109        // Make sure that display ids don't overlap, so there won't be several displays with same
110        // ids among RootWindowContainer children.
111        for (DisplayContent dc : sWm.mRoot.mChildren) {
112            if (dc.getDisplayId() >= sNextDisplayId) {
113                sNextDisplayId = dc.getDisplayId() + 1;
114            }
115        }
116        context.getDisplay().getDisplayInfo(sDisplayInfo);
117        sDisplayContent = createNewDisplay();
118        sWm.mDisplayEnabled = true;
119        sWm.mDisplayReady = true;
120
121        // Set-up some common windows.
122        sWallpaperWindow = createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow");
123        sImeWindow = createWindow(null, TYPE_INPUT_METHOD, sDisplayContent, "sImeWindow");
124        sImeDialogWindow =
125                createWindow(null, TYPE_INPUT_METHOD_DIALOG, sDisplayContent, "sImeDialogWindow");
126        sStatusBarWindow = createWindow(null, TYPE_STATUS_BAR, sDisplayContent, "sStatusBarWindow");
127        sNavBarWindow =
128                createWindow(null, TYPE_NAVIGATION_BAR, sDisplayContent, "sNavBarWindow");
129        sDockedDividerWindow =
130                createWindow(null, TYPE_DOCK_DIVIDER, sDisplayContent, "sDockedDividerWindow");
131        sAppWindow = createWindow(null, TYPE_BASE_APPLICATION, sDisplayContent, "sAppWindow");
132        sChildAppWindowAbove = createWindow(sAppWindow,
133                TYPE_APPLICATION_ATTACHED_DIALOG, sAppWindow.mToken, "sChildAppWindowAbove");
134        sChildAppWindowBelow = createWindow(sAppWindow,
135                TYPE_APPLICATION_MEDIA_OVERLAY, sAppWindow.mToken, "sChildAppWindowBelow");
136    }
137
138    /** Asserts that the first entry is greater than the second entry. */
139    void assertGreaterThan(int first, int second) throws Exception {
140        Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
141    }
142
143    /**
144     * Waits until the main handler for WM has processed all messages.
145     */
146    void waitUntilHandlerIdle() {
147        sWm.mH.runWithScissors(() -> { }, 0);
148    }
149
150    private static WindowToken createWindowToken(DisplayContent dc, int type) {
151        if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
152            return new TestWindowToken(type, dc);
153        }
154
155        final TaskStack stack = createTaskStackOnDisplay(dc);
156        final Task task = createTaskInStack(stack, 0 /* userId */);
157        final TestAppWindowToken token = new TestAppWindowToken(dc);
158        task.addChild(token, 0);
159        return token;
160    }
161
162    static WindowState createWindow(WindowState parent, int type, String name) {
163        return (parent == null)
164                ? createWindow(parent, type, sDisplayContent, name)
165                : createWindow(parent, type, parent.mToken, name);
166    }
167
168    WindowState createAppWindow(Task task, int type, String name) {
169        final AppWindowToken token = new TestAppWindowToken(sDisplayContent);
170        task.addChild(token, 0);
171        return createWindow(null, type, token, name);
172    }
173
174    static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
175        final WindowToken token = createWindowToken(dc, type);
176        return createWindow(parent, type, token, name);
177    }
178
179    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
180        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
181        attrs.setTitle(name);
182
183        final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
184                0, attrs, 0, 0);
185        // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
186        // adding it to the token...
187        token.addWindow(w);
188        return w;
189    }
190
191    /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
192    static TaskStack createTaskStackOnDisplay(DisplayContent dc) {
193        return createStackControllerOnDisplay(dc).mContainer;
194    }
195
196    static StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
197        final int stackId = ++sNextStackId;
198        return new StackWindowController(stackId, null, dc.getDisplayId(),
199                true /* onTop */, new Rect(), sWm);
200    }
201
202    /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
203    static Task createTaskInStack(TaskStack stack, int userId) {
204        final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
205                false, false, new TaskDescription(), null);
206        stack.addTask(newTask, POSITION_TOP);
207        return newTask;
208    }
209
210    /** Creates a {@link DisplayContent} and adds it to the system. */
211    DisplayContent createNewDisplay() {
212        final int displayId = sNextDisplayId++;
213        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
214                sDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
215        return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm));
216    }
217
218    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
219    static class TestWindowToken extends WindowToken {
220
221        TestWindowToken(int type, DisplayContent dc) {
222            this(type, dc, false /* persistOnEmpty */);
223        }
224
225        TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
226            super(sWm, mock(IBinder.class), type, persistOnEmpty, dc);
227        }
228
229        int getWindowsCount() {
230            return mChildren.size();
231        }
232
233        boolean hasWindow(WindowState w) {
234            return mChildren.contains(w);
235        }
236    }
237
238    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
239    static class TestAppWindowToken extends AppWindowToken {
240
241        TestAppWindowToken(DisplayContent dc) {
242            super(sWm, null, false, dc);
243        }
244
245        int getWindowsCount() {
246            return mChildren.size();
247        }
248
249        boolean hasWindow(WindowState w) {
250            return mChildren.contains(w);
251        }
252
253        WindowState getFirstChild() {
254            return mChildren.getFirst();
255        }
256
257        WindowState getLastChild() {
258            return mChildren.getLast();
259        }
260    }
261
262    /* Used so we can gain access to some protected members of the {@link Task} class */
263    class TestTask extends Task {
264
265        boolean mShouldDeferRemoval = false;
266        boolean mOnDisplayChangedCalled = false;
267        private boolean mUseLocalIsAnimating = false;
268        private boolean mIsAnimating = false;
269
270        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
271                Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode,
272                boolean supportsPictureInPicture, boolean homeTask,
273                TaskWindowContainerController controller) {
274            super(taskId, stack, userId, service, bounds, overrideConfig, isOnTopLauncher,
275                    resizeMode, supportsPictureInPicture, homeTask, new TaskDescription(),
276                            controller);
277        }
278
279        boolean shouldDeferRemoval() {
280            return mShouldDeferRemoval;
281        }
282
283        int positionInParent() {
284            return getParent().mChildren.indexOf(this);
285        }
286
287        @Override
288        void onDisplayChanged(DisplayContent dc) {
289            super.onDisplayChanged(dc);
290            mOnDisplayChangedCalled = true;
291        }
292
293        @Override
294        boolean isAnimating() {
295            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
296        }
297
298        void setLocalIsAnimating(boolean isAnimating) {
299            mUseLocalIsAnimating = true;
300            mIsAnimating = isAnimating;
301        }
302    }
303
304    /**
305     * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
306     * class.
307     */
308    class TestTaskWindowContainerController extends TaskWindowContainerController {
309
310        TestTaskWindowContainerController() {
311            this(createStackControllerOnDisplay(sDisplayContent));
312        }
313
314        TestTaskWindowContainerController(StackWindowController stackController) {
315            super(sNextTaskId++, new TaskWindowContainerListener() {
316                        @Override
317                        public void onSnapshotChanged(TaskSnapshot snapshot) {
318
319                        }
320
321                        @Override
322                        public void requestResize(Rect bounds, int resizeMode) {
323
324                        }
325                    }, stackController, 0 /* userId */, null /* bounds */,
326                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
327                    false /* supportsPictureInPicture */, false /* homeTask*/,
328                    false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */,
329                    new TaskDescription(), sWm);
330        }
331
332        @Override
333        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
334                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
335                boolean homeTask, boolean isOnTopLauncher, TaskDescription taskDescription) {
336            return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig,
337                    isOnTopLauncher, resizeMode, supportsPictureInPicture, homeTask, this);
338        }
339    }
340
341    class TestAppWindowContainerController extends AppWindowContainerController {
342
343        final IApplicationToken mToken;
344
345        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
346            this(taskController, new TestIApplicationToken());
347        }
348
349        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
350                IApplicationToken token) {
351            super(taskController, token, null /* listener */, 0 /* index */,
352                    SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */,
353                    true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
354                    false /* launchTaskBehind */, false /* alwaysFocusable */,
355                    0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
356                    0 /* inputDispatchingTimeoutNanos */, sWm);
357            mToken = token;
358        }
359
360        AppWindowToken getAppWindowToken() {
361            return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder());
362        }
363    }
364
365    class TestIApplicationToken implements IApplicationToken {
366
367        private final Binder mBinder = new Binder();
368        @Override
369        public IBinder asBinder() {
370            return mBinder;
371        }
372    }
373
374    /** Used to track resize reports. */
375    class TestWindowState extends WindowState {
376        boolean resizeReported;
377
378        TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
379            super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
380        }
381
382        @Override
383        void reportResized() {
384            super.reportResized();
385            resizeReported = true;
386        }
387
388        @Override
389        public boolean isGoneForLayoutLw() {
390            return false;
391        }
392
393        @Override
394        void updateResizingWindowIfNeeded() {
395            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
396            // the system that it can actually update the window.
397            boolean hadSurface = mHasSurface;
398            mHasSurface = true;
399
400            super.updateResizingWindowIfNeeded();
401
402            mHasSurface = hadSurface;
403        }
404    }
405}
406