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