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