ZOrderingTests.java revision b1579c8d898811010b60eab4e996f3d071980155
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 java.util.HashMap; 20import java.util.LinkedList; 21 22import org.junit.After; 23import org.junit.Assert; 24import org.junit.Before; 25import org.junit.Test; 26import org.junit.runner.RunWith; 27 28import android.view.SurfaceControl; 29import android.view.SurfaceSession; 30import android.util.Log; 31 32import android.platform.test.annotations.Presubmit; 33import android.support.test.filters.SmallTest; 34import android.support.test.runner.AndroidJUnit4; 35 36import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 37import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 38import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 39import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 40import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 41import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 42import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; 43import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 44import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; 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.get(sc); 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 } 125 126 @After 127 public void after() { 128 mTransaction.close(); 129 mParentFor.clear(); 130 } 131 132 LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) { 133 LinkedList<SurfaceControl> p = new LinkedList(); 134 SurfaceControl current = sc; 135 do { 136 p.addLast(current); 137 138 SurfaceControl rs = t.getRelativeLayer(current); 139 if (rs != null) { 140 current = rs; 141 } else { 142 current = mParentFor.get(current); 143 } 144 } while (current != null); 145 return p; 146 } 147 148 void assertZOrderGreaterThan(LayerRecordingTransaction t, 149 SurfaceControl left, SurfaceControl right) throws Exception { 150 final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left); 151 final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right); 152 153 SurfaceControl commonAncestor = null; 154 SurfaceControl leftTop = leftParentChain.peekLast(); 155 SurfaceControl rightTop = rightParentChain.peekLast(); 156 while (leftTop != null && rightTop != null && leftTop == rightTop) { 157 commonAncestor = leftParentChain.removeLast(); 158 rightParentChain.removeLast(); 159 leftTop = leftParentChain.peekLast(); 160 rightTop = rightParentChain.peekLast(); 161 } 162 163 if (rightTop == null) { // right is the parent of left. 164 assertGreaterThan(t.getLayer(leftTop), 0); 165 } else if (leftTop == null) { // left is the parent of right. 166 assertGreaterThan(0, t.getLayer(rightTop)); 167 } else { 168 assertGreaterThan(t.getLayer(leftTop), 169 t.getLayer(rightTop)); 170 } 171 } 172 173 void assertWindowLayerGreaterThan(LayerRecordingTransaction t, 174 WindowState left, WindowState right) throws Exception { 175 assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl()); 176 } 177 178 @Test 179 public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception { 180 sWm.mInputMethodTarget = null; 181 mDisplayContent.assignChildLayers(mTransaction); 182 183 // The Ime has an higher base layer than app windows and lower base layer than system 184 // windows, so it should be above app windows and below system windows if there isn't an IME 185 // target. 186 assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); 187 assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); 188 assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); 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 pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, 312 ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, 313 "pinnedStackWindow"); 314 final WindowState dockedStackWindow = createWindowOnStack(null, 315 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, 316 mDisplayContent, "dockedStackWindow"); 317 final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, 318 ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, 319 mDisplayContent, "assistantStackWindow"); 320 321 mDisplayContent.assignChildLayers(mTransaction); 322 323 assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow); 324 assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); 325 assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); 326 } 327} 328