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