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