WindowTestsBase.java revision daab865344fcfa45fe2789e43462f730a44fba64
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
284        TestWindowToken(int type, DisplayContent dc) {
285            this(type, dc, false /* persistOnEmpty */);
286        }
287
288        TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
289            super(sWm, mock(IBinder.class), type, persistOnEmpty, dc,
290                    false /* ownerCanManageAppTokens */);
291        }
292
293        int getWindowsCount() {
294            return mChildren.size();
295        }
296
297        boolean hasWindow(WindowState w) {
298            return mChildren.contains(w);
299        }
300    }
301
302    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
303    static class TestAppWindowToken extends AppWindowToken {
304
305        TestAppWindowToken(DisplayContent dc) {
306            super(sWm, null, false, dc, true /* fillsParent */);
307        }
308
309        TestAppWindowToken(WindowManagerService service, IApplicationToken token,
310                boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
311                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
312                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
313                boolean alwaysFocusable, AppWindowContainerController controller) {
314            super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
315                    showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
316                    launchTaskBehind, alwaysFocusable, controller);
317        }
318
319        int getWindowsCount() {
320            return mChildren.size();
321        }
322
323        boolean hasWindow(WindowState w) {
324            return mChildren.contains(w);
325        }
326
327        WindowState getFirstChild() {
328            return mChildren.getFirst();
329        }
330
331        WindowState getLastChild() {
332            return mChildren.getLast();
333        }
334
335        int positionInParent() {
336            return getParent().mChildren.indexOf(this);
337        }
338    }
339
340    /* Used so we can gain access to some protected members of the {@link Task} class */
341    class TestTask extends Task {
342
343        boolean mShouldDeferRemoval = false;
344        boolean mOnDisplayChangedCalled = false;
345        private boolean mUseLocalIsAnimating = false;
346        private boolean mIsAnimating = false;
347
348        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
349                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
350                boolean homeTask, TaskWindowContainerController controller) {
351            super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode,
352                    supportsPictureInPicture, homeTask, new TaskDescription(), controller);
353        }
354
355        boolean shouldDeferRemoval() {
356            return mShouldDeferRemoval;
357        }
358
359        int positionInParent() {
360            return getParent().mChildren.indexOf(this);
361        }
362
363        @Override
364        void onDisplayChanged(DisplayContent dc) {
365            super.onDisplayChanged(dc);
366            mOnDisplayChangedCalled = true;
367        }
368
369        @Override
370        boolean isAnimating() {
371            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
372        }
373
374        void setLocalIsAnimating(boolean isAnimating) {
375            mUseLocalIsAnimating = true;
376            mIsAnimating = isAnimating;
377        }
378    }
379
380    /**
381     * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
382     * class.
383     */
384    class TestTaskWindowContainerController extends TaskWindowContainerController {
385
386        TestTaskWindowContainerController() {
387            this(createStackControllerOnDisplay(sDisplayContent));
388        }
389
390        TestTaskWindowContainerController(StackWindowController stackController) {
391            super(sNextTaskId++, new TaskWindowContainerListener() {
392                        @Override
393                        public void onSnapshotChanged(TaskSnapshot snapshot) {
394
395                        }
396
397                        @Override
398                        public void requestResize(Rect bounds, int resizeMode) {
399
400                        }
401                    }, stackController, 0 /* userId */, null /* bounds */,
402                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
403                    false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/,
404                    true /* showForAllUsers */, new TaskDescription(), sWm);
405        }
406
407        @Override
408        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
409                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
410                boolean homeTask, TaskDescription taskDescription) {
411            return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
412                    supportsPictureInPicture, homeTask, this);
413        }
414    }
415
416    class TestAppWindowContainerController extends AppWindowContainerController {
417
418        final IApplicationToken mToken;
419
420        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
421            this(taskController, new TestIApplicationToken());
422        }
423
424        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
425                IApplicationToken token) {
426            super(taskController, token, null /* listener */, 0 /* index */,
427                    SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */,
428                    true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
429                    false /* launchTaskBehind */, false /* alwaysFocusable */,
430                    0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
431                    0 /* inputDispatchingTimeoutNanos */, sWm);
432            mToken = token;
433        }
434
435        @Override
436        AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
437                boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
438                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
439                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
440                boolean alwaysFocusable, AppWindowContainerController controller) {
441            return new TestAppWindowToken(service, token, voiceInteraction, dc,
442                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
443                    orientation,
444                    rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
445                    controller);
446        }
447
448        AppWindowToken getAppWindowToken() {
449            return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder());
450        }
451    }
452
453    class TestIApplicationToken implements IApplicationToken {
454
455        private final Binder mBinder = new Binder();
456        @Override
457        public IBinder asBinder() {
458            return mBinder;
459        }
460    }
461
462    /** Used to track resize reports. */
463    class TestWindowState extends WindowState {
464        boolean resizeReported;
465
466        TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
467            super(sWm, sMockSession, new TestIWindow(), token, null, OP_NONE, 0, attrs, 0, 0,
468                    false /* ownerCanAddInternalSystemWindow */);
469            sWm.mWindowMap.put(mClient.asBinder(), this);
470        }
471
472        @Override
473        void reportResized() {
474            super.reportResized();
475            resizeReported = true;
476        }
477
478        @Override
479        public boolean isGoneForLayoutLw() {
480            return false;
481        }
482
483        @Override
484        void updateResizingWindowIfNeeded() {
485            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
486            // the system that it can actually update the window.
487            boolean hadSurface = mHasSurface;
488            mHasSurface = true;
489
490            super.updateResizingWindowIfNeeded();
491
492            mHasSurface = hadSurface;
493        }
494    }
495}
496