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