WindowTestsBase.java revision 7fbeb8a5d754c7e5c330458cf241c5e2a718099c
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    /** Asserts that the first entry is greater than the second entry. */
162    void assertGreaterThan(int first, int second) throws Exception {
163        Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
164    }
165
166    /**
167     * Waits until the main handler for WM has processed all messages.
168     */
169    void waitUntilHandlerIdle() {
170        sWm.mH.runWithScissors(() -> { }, 0);
171    }
172
173    private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) {
174        if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
175            return new TestWindowToken(type, dc);
176        }
177
178        final TaskStack stack = stackId == INVALID_STACK_ID
179                ? createTaskStackOnDisplay(dc)
180                : createStackControllerOnStackOnDisplay(stackId, dc).mContainer;
181        final Task task = createTaskInStack(stack, 0 /* userId */);
182        final TestAppWindowToken token = new TestAppWindowToken(dc);
183        task.addChild(token, 0);
184        return token;
185    }
186
187    static WindowState createWindow(WindowState parent, int type, String name) {
188        return (parent == null)
189                ? createWindow(parent, type, sDisplayContent, name)
190                : createWindow(parent, type, parent.mToken, name);
191    }
192
193    static WindowState createWindowOnStack(WindowState parent, int stackId, int type,
194            DisplayContent dc, String name) {
195        final WindowToken token = createWindowToken(dc, stackId, type);
196        return createWindow(parent, type, token, name);
197    }
198
199    WindowState createAppWindow(Task task, int type, String name) {
200        final AppWindowToken token = new TestAppWindowToken(sDisplayContent);
201        task.addChild(token, 0);
202        return createWindow(null, type, token, name);
203    }
204
205    static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
206        final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type);
207        return createWindow(parent, type, token, name);
208    }
209
210    static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
211            boolean ownerCanAddInternalSystemWindow) {
212        final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type);
213        return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow);
214    }
215
216    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
217        return createWindow(parent, type, token, name, false /* ownerCanAddInternalSystemWindow */);
218    }
219
220    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
221            boolean ownerCanAddInternalSystemWindow) {
222        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
223        attrs.setTitle(name);
224
225        final WindowState w = new WindowState(sWm, sMockSession, new TestIWindow(), token, parent,
226                OP_NONE, 0, attrs, 0, 0, ownerCanAddInternalSystemWindow);
227        // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
228        // adding it to the token...
229        token.addWindow(w);
230        sWm.mWindowMap.put(w.mClient.asBinder(), w);
231        return w;
232    }
233
234    /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
235    static TaskStack createTaskStackOnDisplay(DisplayContent dc) {
236        return createStackControllerOnDisplay(dc).mContainer;
237    }
238
239    static StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
240        final int stackId = ++sNextStackId;
241        return createStackControllerOnStackOnDisplay(stackId, dc);
242    }
243
244    static StackWindowController createStackControllerOnStackOnDisplay(int stackId,
245            DisplayContent dc) {
246        return new StackWindowController(stackId, null, dc.getDisplayId(),
247                true /* onTop */, new Rect(), sWm);
248    }
249
250    /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
251    static Task createTaskInStack(TaskStack stack, int userId) {
252        final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, 0, false,
253                false, new TaskDescription(), null);
254        stack.addTask(newTask, POSITION_TOP);
255        return newTask;
256    }
257
258    /** Creates a {@link DisplayContent} and adds it to the system. */
259    DisplayContent createNewDisplay() {
260        final int displayId = sNextDisplayId++;
261        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
262                sDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
263        return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm));
264    }
265
266    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
267    static class TestWindowToken extends WindowToken {
268        int adj = 0;
269
270        TestWindowToken(int type, DisplayContent dc) {
271            this(type, dc, false /* persistOnEmpty */);
272        }
273
274        TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
275            super(sWm, mock(IBinder.class), type, persistOnEmpty, dc,
276                    false /* ownerCanManageAppTokens */);
277        }
278
279        int getWindowsCount() {
280            return mChildren.size();
281        }
282
283        boolean hasWindow(WindowState w) {
284            return mChildren.contains(w);
285        }
286
287        @Override
288        int getAnimLayerAdjustment() {
289            return adj;
290        }
291    }
292
293    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
294    static class TestAppWindowToken extends AppWindowToken {
295
296        TestAppWindowToken(DisplayContent dc) {
297            super(sWm, null, false, dc, true /* fillsParent */);
298        }
299
300        TestAppWindowToken(WindowManagerService service, IApplicationToken token,
301                boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
302                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
303                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
304                boolean alwaysFocusable, AppWindowContainerController controller) {
305            super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
306                    showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
307                    launchTaskBehind, alwaysFocusable, controller);
308        }
309
310        int getWindowsCount() {
311            return mChildren.size();
312        }
313
314        boolean hasWindow(WindowState w) {
315            return mChildren.contains(w);
316        }
317
318        WindowState getFirstChild() {
319            return mChildren.getFirst();
320        }
321
322        WindowState getLastChild() {
323            return mChildren.getLast();
324        }
325
326        int positionInParent() {
327            return getParent().mChildren.indexOf(this);
328        }
329    }
330
331    /* Used so we can gain access to some protected members of the {@link Task} class */
332    class TestTask extends Task {
333
334        boolean mShouldDeferRemoval = false;
335        boolean mOnDisplayChangedCalled = false;
336        private boolean mUseLocalIsAnimating = false;
337        private boolean mIsAnimating = false;
338
339        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
340                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
341                boolean homeTask, TaskWindowContainerController controller) {
342            super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode,
343                    supportsPictureInPicture, homeTask, new TaskDescription(), controller);
344        }
345
346        boolean shouldDeferRemoval() {
347            return mShouldDeferRemoval;
348        }
349
350        int positionInParent() {
351            return getParent().mChildren.indexOf(this);
352        }
353
354        @Override
355        void onDisplayChanged(DisplayContent dc) {
356            super.onDisplayChanged(dc);
357            mOnDisplayChangedCalled = true;
358        }
359
360        @Override
361        boolean isAnimating() {
362            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
363        }
364
365        void setLocalIsAnimating(boolean isAnimating) {
366            mUseLocalIsAnimating = true;
367            mIsAnimating = isAnimating;
368        }
369    }
370
371    /**
372     * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
373     * class.
374     */
375    class TestTaskWindowContainerController extends TaskWindowContainerController {
376
377        TestTaskWindowContainerController() {
378            this(createStackControllerOnDisplay(sDisplayContent));
379        }
380
381        TestTaskWindowContainerController(StackWindowController stackController) {
382            super(sNextTaskId++, new TaskWindowContainerListener() {
383                        @Override
384                        public void onSnapshotChanged(TaskSnapshot snapshot) {
385
386                        }
387
388                        @Override
389                        public void requestResize(Rect bounds, int resizeMode) {
390
391                        }
392                    }, stackController, 0 /* userId */, null /* bounds */,
393                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
394                    false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/,
395                    true /* showForAllUsers */, new TaskDescription(), sWm);
396        }
397
398        @Override
399        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
400                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
401                boolean homeTask, TaskDescription taskDescription) {
402            return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
403                    supportsPictureInPicture, homeTask, this);
404        }
405    }
406
407    class TestAppWindowContainerController extends AppWindowContainerController {
408
409        final IApplicationToken mToken;
410
411        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
412            this(taskController, new TestIApplicationToken());
413        }
414
415        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
416                IApplicationToken token) {
417            super(taskController, token, null /* listener */, 0 /* index */,
418                    SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */,
419                    true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
420                    false /* launchTaskBehind */, false /* alwaysFocusable */,
421                    0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
422                    0 /* inputDispatchingTimeoutNanos */, sWm);
423            mToken = token;
424        }
425
426        @Override
427        AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
428                boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
429                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
430                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
431                boolean alwaysFocusable, AppWindowContainerController controller) {
432            return new TestAppWindowToken(service, token, voiceInteraction, dc,
433                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
434                    orientation,
435                    rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
436                    controller);
437        }
438
439        AppWindowToken getAppWindowToken() {
440            return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder());
441        }
442    }
443
444    class TestIApplicationToken implements IApplicationToken {
445
446        private final Binder mBinder = new Binder();
447        @Override
448        public IBinder asBinder() {
449            return mBinder;
450        }
451    }
452
453    /** Used to track resize reports. */
454    class TestWindowState extends WindowState {
455        boolean resizeReported;
456
457        TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
458            super(sWm, sMockSession, new TestIWindow(), token, null, OP_NONE, 0, attrs, 0, 0,
459                    false /* ownerCanAddInternalSystemWindow */);
460            sWm.mWindowMap.put(mClient.asBinder(), this);
461        }
462
463        @Override
464        void reportResized() {
465            super.reportResized();
466            resizeReported = true;
467        }
468
469        @Override
470        public boolean isGoneForLayoutLw() {
471            return false;
472        }
473
474        @Override
475        void updateResizingWindowIfNeeded() {
476            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
477            // the system that it can actually update the window.
478            boolean hadSurface = mHasSurface;
479            mHasSurface = true;
480
481            super.updateResizingWindowIfNeeded();
482
483            mHasSurface = hadSurface;
484        }
485    }
486}
487