ZOrderingTests.java revision 11408826496a2c7a8b23e97ec3340972b0df8c36
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 static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
20import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
23import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
24import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
25import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
26import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
27import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
28import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
29import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
30import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
31import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
32import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
33import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
34
35import android.platform.test.annotations.Presubmit;
36import android.support.test.filters.SmallTest;
37import android.support.test.runner.AndroidJUnit4;
38import android.view.SurfaceControl;
39import android.view.SurfaceSession;
40
41import org.junit.After;
42import org.junit.Test;
43import org.junit.runner.RunWith;
44
45import java.util.HashMap;
46import java.util.LinkedList;
47
48/**
49 * Tests for the {@link WindowLayersController} class.
50 *
51 * Build/Install/Run:
52 *  bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
53 */
54@SmallTest
55@Presubmit
56@RunWith(AndroidJUnit4.class)
57public class ZOrderingTests extends WindowTestsBase {
58
59    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
60        HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
61        HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
62
63        @Override
64        public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
65            mRelativeLayersForControl.remove(sc);
66            mLayersForControl.put(sc, layer);
67            return super.setLayer(sc, layer);
68        }
69
70        @Override
71        public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
72                SurfaceControl relativeTo,
73                int layer) {
74            mRelativeLayersForControl.put(sc, relativeTo);
75            mLayersForControl.put(sc, layer);
76            return super.setRelativeLayer(sc, relativeTo, layer);
77        }
78
79        int getLayer(SurfaceControl sc) {
80            return mLayersForControl.getOrDefault(sc, 0);
81        }
82
83        SurfaceControl getRelativeLayer(SurfaceControl sc) {
84            return mRelativeLayersForControl.get(sc);
85        }
86    };
87
88    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
89    // such that we can keep track of the parents of Surfaces as they are constructed.
90    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
91
92    private class HierarchyRecorder extends SurfaceControl.Builder {
93        SurfaceControl mPendingParent;
94
95        HierarchyRecorder(SurfaceSession s) {
96            super(s);
97        }
98
99        public SurfaceControl.Builder setParent(SurfaceControl sc) {
100            mPendingParent = sc;
101            return super.setParent(sc);
102        }
103        public SurfaceControl build() {
104            SurfaceControl sc = super.build();
105            mParentFor.put(sc, mPendingParent);
106            mPendingParent = null;
107            return sc;
108        }
109    };
110
111    class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
112        public SurfaceControl.Builder make(SurfaceSession s) {
113            return new HierarchyRecorder(s);
114        }
115    };
116
117    private LayerRecordingTransaction mTransaction;
118
119    @Override
120    void beforeCreateDisplay() {
121        // We can't use @Before here because it may happen after WindowTestsBase @Before
122        // which is after construction of the DisplayContent, meaning the HierarchyRecorder
123        // would miss construction of the top-level layers.
124        mTransaction = new LayerRecordingTransaction();
125        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
126        sWm.mTransactionFactory = () -> mTransaction;
127    }
128
129    @After
130    public void after() {
131        mTransaction.close();
132        mParentFor.clear();
133    }
134
135    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
136        LinkedList<SurfaceControl> p = new LinkedList();
137        SurfaceControl current = sc;
138        do {
139            p.addLast(current);
140
141            SurfaceControl rs = t.getRelativeLayer(current);
142            if (rs != null) {
143                current = rs;
144            } else {
145                current = mParentFor.get(current);
146            }
147        } while (current != null);
148        return p;
149    }
150
151    void assertZOrderGreaterThan(LayerRecordingTransaction t,
152            SurfaceControl left, SurfaceControl right) throws Exception {
153        final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
154        final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
155
156        SurfaceControl commonAncestor = null;
157        SurfaceControl leftTop = leftParentChain.peekLast();
158        SurfaceControl rightTop = rightParentChain.peekLast();
159        while (leftTop != null && rightTop != null && leftTop == rightTop) {
160            commonAncestor = leftParentChain.removeLast();
161            rightParentChain.removeLast();
162            leftTop = leftParentChain.peekLast();
163            rightTop = rightParentChain.peekLast();
164        }
165
166        if (rightTop == null) { // right is the parent of left.
167            assertGreaterThan(t.getLayer(leftTop), 0);
168        } else if (leftTop == null) { // left is the parent of right.
169            assertGreaterThan(0, t.getLayer(rightTop));
170        } else {
171            assertGreaterThan(t.getLayer(leftTop),
172                    t.getLayer(rightTop));
173        }
174    }
175
176    void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
177            WindowState left, WindowState right) throws Exception {
178        assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
179    }
180
181    @Test
182    public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
183        sWm.mInputMethodTarget = null;
184        mDisplayContent.assignChildLayers(mTransaction);
185
186        // The Ime has an higher base layer than app windows and lower base layer than system
187        // windows, so it should be above app windows and below system windows if there isn't an IME
188        // target.
189        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
190        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
191        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
192        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
193
194        // And, IME dialogs should always have an higher layer than the IME.
195        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
196    }
197
198    @Test
199    public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
200        final WindowState imeAppTarget =
201                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
202        sWm.mInputMethodTarget = imeAppTarget;
203        mDisplayContent.assignChildLayers(mTransaction);
204
205        // Ime should be above all app windows and below system windows if it is targeting an app
206        // window.
207        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
208        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
209        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
210        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
211        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
212
213        // And, IME dialogs should always have an higher layer than the IME.
214        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
215    }
216
217    @Test
218    public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
219        final WindowState imeAppTarget =
220                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
221        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
222                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
223                "imeAppTargetChildAboveWindow");
224        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
225                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
226                "imeAppTargetChildBelowWindow");
227
228        sWm.mInputMethodTarget = imeAppTarget;
229        mDisplayContent.assignChildLayers(mTransaction);
230
231        // Ime should be above all app windows except for child windows that are z-ordered above it
232        // and below system windows if it is targeting an app window.
233        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
234        assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
235        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
236        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
237        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
238        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
239
240        // And, IME dialogs should always have an higher layer than the IME.
241        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
242    }
243
244    @Test
245    public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
246        final WindowState appBelowImeTarget =
247                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
248        final WindowState imeAppTarget =
249                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
250        final WindowState appAboveImeTarget =
251                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
252
253        sWm.mInputMethodTarget = imeAppTarget;
254        mDisplayContent.assignChildLayers(mTransaction);
255
256        // Ime should be above all app windows except for non-fullscreen app window above it and
257        // below system windows if it is targeting an app window.
258        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
259        assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
260        assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
261        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
262        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
263        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
264        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
265
266        // And, IME dialogs should always have an higher layer than the IME.
267        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
268    }
269
270    @Test
271    public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
272        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
273                mDisplayContent, "imeSystemOverlayTarget",
274                true /* ownerCanAddInternalSystemWindow */);
275
276        sWm.mInputMethodTarget = imeSystemOverlayTarget;
277        mDisplayContent.assignChildLayers(mTransaction);
278
279        // The IME target base layer is higher than all window except for the nav bar window, so the
280        // IME should be above all windows except for the nav bar.
281        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
282        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
283        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
284        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
285
286        // The IME has a higher base layer than the status bar so we may expect it to go
287        // above the status bar once they are both in the Non-App layer, as past versions of this
288        // test enforced. However this seems like the wrong behavior unless the status bar is the
289        // IME target.
290        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
291        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
292
293        // And, IME dialogs should always have an higher layer than the IME.
294        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
295    }
296
297    @Test
298    public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
299        sWm.mInputMethodTarget = mStatusBarWindow;
300        mDisplayContent.assignChildLayers(mTransaction);
301
302        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
303        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
304        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
305        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
306
307        // And, IME dialogs should always have an higher layer than the IME.
308        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
309    }
310
311    @Test
312    public void testStackLayers() throws Exception {
313        final WindowState anyWindow1 =
314                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
315        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
316                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
317                "pinnedStackWindow");
318        final WindowState dockedStackWindow = createWindowOnStack(null,
319                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
320                mDisplayContent, "dockedStackWindow");
321        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
322                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
323                mDisplayContent, "assistantStackWindow");
324        final WindowState anyWindow2 =
325                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
326
327        mDisplayContent.assignChildLayers(mTransaction);
328
329        // We compare the split-screen windowing mode to two different normal windowing
330        // mode windows added before and after it to ensure the correct Z ordering irrespective
331        // of ordering in the child list.
332        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, anyWindow1);
333        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, anyWindow2);
334        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
335        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
336    }
337
338    @Test
339    public void testAssignWindowLayers_ForSysUiPanels() throws Exception {
340        final WindowState navBarPanel =
341                createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel");
342        final WindowState statusBarPanel =
343                createWindow(null, TYPE_STATUS_BAR_PANEL, mDisplayContent, "StatusBarPanel");
344        final WindowState statusBarSubPanel =
345                createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel");
346        mDisplayContent.assignChildLayers(mTransaction);
347
348        // Ime should be above all app windows and below system windows if it is targeting an app
349        // window.
350        assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow);
351        assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
352        assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
353    }
354
355    @Test
356    public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() throws Exception {
357        // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
358        // then we can drop all negative layering on the windowing side.
359
360        final WindowState anyWindow =
361                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
362        final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
363                "TypeApplicationMediaChild");
364        final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
365                mDisplayContent, "TypeApplicationMediaOverlayChild");
366
367        mDisplayContent.assignChildLayers(mTransaction);
368
369        assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild);
370        assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child);
371    }
372
373    @Test
374    public void testDockedDividerPosition() throws Exception {
375        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
376                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
377                "pinnedStackWindow");
378        final WindowState splitScreenWindow = createWindowOnStack(null,
379                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
380                mDisplayContent, "splitScreenWindow");
381        final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
382                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
383                TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
384        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
385                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
386                mDisplayContent, "assistantStackWindow");
387        final WindowState dockedDividerWindow = createWindow(null, TYPE_DOCK_DIVIDER,
388                mDisplayContent, "dockedDivider");
389
390        mDisplayContent.assignChildLayers(mTransaction);
391
392        assertWindowLayerGreaterThan(mTransaction, dockedDividerWindow, splitScreenWindow);
393        assertWindowLayerGreaterThan(mTransaction, dockedDividerWindow, splitScreenSecondaryWindow);
394        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedDividerWindow);
395        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, dockedDividerWindow);
396    }
397}
398