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