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