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