1 /* 2 * Copyright (C) 2018 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 17 package com.android.server.wm; 18 19 import static android.view.InsetsState.ITYPE_CLIMATE_BAR; 20 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 21 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 22 import static android.view.InsetsState.ITYPE_STATUS_BAR; 23 import static android.view.InsetsState.ITYPE_TOP_GESTURES; 24 import static android.view.Surface.ROTATION_0; 25 import static android.view.Surface.ROTATION_270; 26 import static android.view.Surface.ROTATION_90; 27 import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; 28 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 29 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 30 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 31 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 32 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; 33 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 34 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; 35 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 38 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 39 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 40 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; 41 import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM; 42 import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT; 43 import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT; 44 import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP; 45 46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 48 49 import static org.hamcrest.Matchers.is; 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertNotEquals; 52 import static org.junit.Assert.assertThat; 53 import static org.mockito.ArgumentMatchers.any; 54 import static org.mockito.ArgumentMatchers.eq; 55 import static org.mockito.Mockito.doNothing; 56 import static org.mockito.Mockito.spy; 57 import static org.testng.Assert.expectThrows; 58 59 import android.graphics.Insets; 60 import android.graphics.Rect; 61 import android.platform.test.annotations.Presubmit; 62 import android.util.Pair; 63 import android.util.SparseArray; 64 import android.view.DisplayCutout; 65 import android.view.DisplayInfo; 66 import android.view.Gravity; 67 import android.view.InsetsState; 68 import android.view.InsetsVisibilities; 69 import android.view.PrivacyIndicatorBounds; 70 import android.view.RoundedCorners; 71 import android.view.WindowInsets.Side; 72 import android.view.WindowInsets.Type; 73 74 import androidx.test.filters.SmallTest; 75 76 import com.android.server.wm.utils.WmDisplayCutout; 77 78 import org.junit.Before; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 82 import java.io.PrintWriter; 83 import java.io.StringWriter; 84 85 /** 86 * Tests for the {@link DisplayPolicy} class. 87 * 88 * Build/Install/Run: 89 * atest WmTests:DisplayPolicyLayoutTests 90 */ 91 @SmallTest 92 @Presubmit 93 @WindowTestsBase.UseTestDisplay( 94 addWindows = { WindowTestsBase.W_STATUS_BAR, WindowTestsBase.W_NAVIGATION_BAR }) 95 @RunWith(WindowTestRunner.class) 96 public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 97 98 private DisplayFrames mFrames; 99 private WindowState mWindow; 100 private int mRotation = ROTATION_0; 101 private boolean mHasDisplayCutout; 102 private boolean mIsLongEdgeDisplayCutout; 103 private boolean mHasRoundedCorners; 104 105 private final Rect mDisplayBounds = new Rect(); 106 107 @Before setUp()108 public void setUp() throws Exception { 109 mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); 110 // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from 111 // changing those frames. 112 doNothing().when(mWindow).computeFrame(any()); 113 114 spyOn(mStatusBarWindow); 115 spyOn(mNavBarWindow); 116 117 // Disabling this call for most tests since it can override the systemUiFlags when called. 118 doNothing().when(mDisplayPolicy).updateSystemBarAttributes(); 119 120 updateDisplayFrames(); 121 } 122 addWindowWithRawInsetsState(WindowState win)123 void addWindowWithRawInsetsState(WindowState win) { 124 addWindow(win); 125 // Without mPerformLayout in display content, the window cannot see any insets. Override the 126 // insets state with the global one. 127 final InsetsState insetsState = 128 win.getDisplayContent().getInsetsStateController().getRawInsetsState(); 129 win.mAboveInsetsState.set(insetsState); 130 } 131 setRotation(int rotation, boolean includingWindows)132 public void setRotation(int rotation, boolean includingWindows) { 133 mRotation = rotation; 134 updateDisplayFrames(); 135 if (includingWindows) { 136 mNavBarWindow.getWindowConfiguration().setRotation(rotation); 137 mStatusBarWindow.getWindowConfiguration().setRotation(rotation); 138 mWindow.getWindowConfiguration().setRotation(rotation); 139 } 140 } 141 addDisplayCutout()142 public void addDisplayCutout() { 143 mHasDisplayCutout = true; 144 updateDisplayFrames(); 145 } 146 addLongEdgeDisplayCutout()147 public void addLongEdgeDisplayCutout() { 148 mHasDisplayCutout = true; 149 mIsLongEdgeDisplayCutout = true; 150 updateDisplayFrames(); 151 } 152 addRoundedCorners()153 public void addRoundedCorners() { 154 mHasRoundedCorners = true; 155 updateDisplayFrames(); 156 } 157 updateDisplayFrames()158 private void updateDisplayFrames() { 159 mFrames = createDisplayFrames( 160 mDisplayContent.getInsetsStateController().getRawInsetsState()); 161 mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight); 162 mDisplayContent.mDisplayFrames = mFrames; 163 mDisplayContent.setBounds(mDisplayBounds); 164 mDisplayPolicy.layoutWindowLw(mNavBarWindow, null, mFrames); 165 mDisplayPolicy.layoutWindowLw(mStatusBarWindow, null, mFrames); 166 } 167 createDisplayFrames(InsetsState insetsState)168 private DisplayFrames createDisplayFrames(InsetsState insetsState) { 169 final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation, 170 mHasDisplayCutout, mIsLongEdgeDisplayCutout); 171 final RoundedCorners roundedCorners = mHasRoundedCorners 172 ? mDisplayContent.calculateRoundedCornersForRotation(mRotation) 173 : RoundedCorners.NO_ROUNDED_CORNERS; 174 return new DisplayFrames(mDisplayContent.getDisplayId(), 175 insetsState, info.first, info.second, roundedCorners, new PrivacyIndicatorBounds()); 176 } 177 178 @Test addingWindow_doesNotTamperWithSysuiFlags()179 public void addingWindow_doesNotTamperWithSysuiFlags() { 180 mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 181 addWindow(mWindow); 182 183 assertEquals(0, mWindow.mAttrs.systemUiVisibility); 184 assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility); 185 } 186 187 @Test addingWindow_withInsetsTypes()188 public void addingWindow_withInsetsTypes() { 189 mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. 190 191 WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); 192 win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES}; 193 win.getFrame().set(0, 0, 500, 100); 194 195 addWindow(win); 196 InsetsStateController controller = mDisplayContent.getInsetsStateController(); 197 controller.onPostLayout(); 198 199 InsetsSourceProvider statusBarProvider = controller.getSourceProvider(ITYPE_STATUS_BAR); 200 assertEquals(new Rect(0, 0, 500, 100), statusBarProvider.getSource().getFrame()); 201 assertEquals(Insets.of(0, 100, 0, 0), 202 statusBarProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), 203 false /* ignoreVisibility */)); 204 205 InsetsSourceProvider topGesturesProvider = controller.getSourceProvider(ITYPE_TOP_GESTURES); 206 assertEquals(new Rect(0, 0, 500, 100), topGesturesProvider.getSource().getFrame()); 207 assertEquals(Insets.of(0, 100, 0, 0), 208 topGesturesProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), 209 false /* ignoreVisibility */)); 210 211 InsetsSourceProvider navigationBarProvider = controller.getSourceProvider( 212 ITYPE_NAVIGATION_BAR); 213 assertNotEquals(new Rect(0, 0, 500, 100), navigationBarProvider.getSource().getFrame()); 214 } 215 216 @Test addingWindow_InWindowTypeWithPredefinedInsets()217 public void addingWindow_InWindowTypeWithPredefinedInsets() { 218 mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. 219 WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar"); 220 win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; 221 win.getFrame().set(0, 0, 500, 100); 222 223 addWindow(win); 224 mDisplayContent.getInsetsStateController().onPostLayout(); 225 226 InsetsSourceProvider provider = 227 mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR); 228 if (INSETS_LAYOUT_GENERALIZATION) { 229 // In the new flexible insets setup, the insets frame should always respect the window 230 // layout result. 231 assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); 232 } else { 233 assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); 234 } 235 } 236 237 @Test addingWindow_throwsException_WithMultipleInsetTypes()238 public void addingWindow_throwsException_WithMultipleInsetTypes() { 239 WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); 240 win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}; 241 242 expectThrows(IllegalArgumentException.class, () -> addWindow(win1)); 243 244 WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); 245 win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; 246 247 expectThrows(IllegalArgumentException.class, () -> addWindow(win2)); 248 } 249 250 @Test addingWindow_variousGravities_alternateBarPosUpdated()251 public void addingWindow_variousGravities_alternateBarPosUpdated() { 252 mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one. 253 254 WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1"); 255 win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; 256 win1.mAttrs.gravity = Gravity.TOP; 257 win1.getFrame().set(0, 0, 200, 500); 258 addWindow(win1); 259 260 assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP); 261 mDisplayPolicy.removeWindowLw(win1); 262 263 WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2"); 264 win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; 265 win2.mAttrs.gravity = Gravity.BOTTOM; 266 win2.getFrame().set(0, 0, 200, 500); 267 addWindow(win2); 268 269 assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM); 270 mDisplayPolicy.removeWindowLw(win2); 271 272 WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3"); 273 win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; 274 win3.mAttrs.gravity = Gravity.LEFT; 275 win3.getFrame().set(0, 0, 200, 500); 276 addWindow(win3); 277 278 assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT); 279 mDisplayPolicy.removeWindowLw(win3); 280 281 WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4"); 282 win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; 283 win4.mAttrs.gravity = Gravity.RIGHT; 284 win4.getFrame().set(0, 0, 200, 500); 285 addWindow(win4); 286 287 assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT); 288 mDisplayPolicy.removeWindowLw(win4); 289 } 290 291 @Test layoutWindowLw_fitStatusBars()292 public void layoutWindowLw_fitStatusBars() { 293 mWindow.mAttrs.setFitInsetsTypes(Type.statusBars()); 294 addWindowWithRawInsetsState(mWindow); 295 296 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 297 298 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); 299 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); 300 } 301 302 @Test layoutWindowLw_fitNavigationBars()303 public void layoutWindowLw_fitNavigationBars() { 304 mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars()); 305 addWindowWithRawInsetsState(mWindow); 306 307 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 308 309 assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, NAV_BAR_HEIGHT); 310 assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); 311 } 312 313 @Test layoutWindowLw_fitAllSides()314 public void layoutWindowLw_fitAllSides() { 315 mWindow.mAttrs.setFitInsetsSides(Side.all()); 316 addWindowWithRawInsetsState(mWindow); 317 318 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 319 320 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 321 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 322 } 323 324 @Test layoutWindowLw_fitTopOnly()325 public void layoutWindowLw_fitTopOnly() { 326 mWindow.mAttrs.setFitInsetsSides(Side.TOP); 327 addWindowWithRawInsetsState(mWindow); 328 329 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 330 331 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); 332 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); 333 } 334 335 @Test layoutWindowLw_fitInsetsIgnoringVisibility()336 public void layoutWindowLw_fitInsetsIgnoringVisibility() { 337 final InsetsState state = 338 mDisplayContent.getInsetsStateController().getRawInsetsState(); 339 state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); 340 state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); 341 mWindow.mAttrs.setFitInsetsIgnoringVisibility(true); 342 addWindowWithRawInsetsState(mWindow); 343 344 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 345 346 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 347 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 348 } 349 350 @Test layoutWindowLw_fitInsetsNotIgnoringVisibility()351 public void layoutWindowLw_fitInsetsNotIgnoringVisibility() { 352 final InsetsState state = 353 mDisplayContent.getInsetsStateController().getRawInsetsState(); 354 state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); 355 state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false); 356 mWindow.mAttrs.setFitInsetsIgnoringVisibility(false); 357 addWindowWithRawInsetsState(mWindow); 358 359 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 360 361 assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); 362 assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); 363 } 364 365 @Test layoutWindowLw_insetParentFrameByIme()366 public void layoutWindowLw_insetParentFrameByIme() { 367 final InsetsState state = 368 mDisplayContent.getInsetsStateController().getRawInsetsState(); 369 state.getSource(InsetsState.ITYPE_IME).setVisible(true); 370 state.getSource(InsetsState.ITYPE_IME).setFrame( 371 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); 372 mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; 373 addWindowWithRawInsetsState(mWindow); 374 375 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 376 377 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 378 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT); 379 } 380 381 @Test layoutWindowLw_fitDisplayCutout()382 public void layoutWindowLw_fitDisplayCutout() { 383 addDisplayCutout(); 384 385 mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout()); 386 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 387 addWindowWithRawInsetsState(mWindow); 388 389 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 390 391 assertInsetByTopBottom(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0); 392 assertInsetByTopBottom(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0); 393 } 394 395 @Test layoutWindowLw_withDisplayCutout()396 public void layoutWindowLw_withDisplayCutout() { 397 addDisplayCutout(); 398 399 mWindow.mAttrs.flags = 400 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 401 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 402 addWindowWithRawInsetsState(mWindow); 403 404 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 405 406 assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); 407 assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); 408 } 409 410 @Test layoutWindowLw_withDisplayCutout_never()411 public void layoutWindowLw_withDisplayCutout_never() { 412 addDisplayCutout(); 413 414 mWindow.mAttrs.flags = 415 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 416 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 417 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; 418 addWindowWithRawInsetsState(mWindow); 419 420 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 421 422 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); 423 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); 424 } 425 426 @Test layoutWindowLw_withDisplayCutout_shortEdges()427 public void layoutWindowLw_withDisplayCutout_shortEdges() { 428 addDisplayCutout(); 429 430 mWindow.mAttrs.flags = 431 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 432 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 433 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 434 addWindowWithRawInsetsState(mWindow); 435 436 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 437 438 assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); 439 assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); 440 } 441 442 @Test layoutWindowLw_withDisplayCutout_always()443 public void layoutWindowLw_withDisplayCutout_always() { 444 addDisplayCutout(); 445 446 mWindow.mAttrs.flags = 447 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 448 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 449 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 450 addWindowWithRawInsetsState(mWindow); 451 452 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 453 454 assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); 455 assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); 456 } 457 458 @Test layoutWindowLw_withDisplayCutout_layoutFullscreen()459 public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { 460 addDisplayCutout(); 461 462 mWindow.mAttrs.flags = 463 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 464 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 465 addWindowWithRawInsetsState(mWindow); 466 467 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 468 469 assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); 470 assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); 471 } 472 473 @Test layoutWindowLw_withDisplayCutout_fullscreen()474 public void layoutWindowLw_withDisplayCutout_fullscreen() { 475 addDisplayCutout(); 476 477 mWindow.mAttrs.flags = 478 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 479 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 480 mDisplayContent.getInsetsStateController().getRawInsetsState() 481 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); 482 final InsetsVisibilities requestedVisibilities = new InsetsVisibilities(); 483 requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false); 484 mWindow.setRequestedVisibilities(requestedVisibilities); 485 addWindowWithRawInsetsState(mWindow); 486 487 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 488 489 assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); 490 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); 491 } 492 493 @Test layoutWindowLw_withDisplayCutout_fullscreenInCutout()494 public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { 495 addDisplayCutout(); 496 497 mWindow.mAttrs.flags = 498 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 499 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 500 mDisplayContent.getInsetsStateController().getRawInsetsState() 501 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); 502 final InsetsVisibilities requestedVisibilities = new InsetsVisibilities(); 503 requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false); 504 mWindow.setRequestedVisibilities(requestedVisibilities); 505 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 506 addWindowWithRawInsetsState(mWindow); 507 508 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 509 510 assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); 511 assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); 512 } 513 514 515 @Test layoutWindowLw_withDisplayCutout_landscape()516 public void layoutWindowLw_withDisplayCutout_landscape() { 517 addDisplayCutout(); 518 setRotation(ROTATION_90, true /* includingWindows */); 519 mWindow.mAttrs.flags = 520 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 521 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 522 addWindowWithRawInsetsState(mWindow); 523 524 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 525 526 assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 527 assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 528 } 529 530 @Test layoutWindowLw_withDisplayCutout_seascape()531 public void layoutWindowLw_withDisplayCutout_seascape() { 532 addDisplayCutout(); 533 setRotation(ROTATION_270, true /* includingWindows */); 534 535 mWindow.mAttrs.flags = 536 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 537 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 538 addWindowWithRawInsetsState(mWindow); 539 540 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 541 542 assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); 543 assertInsetBy(mWindow.getDisplayFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); 544 } 545 546 @Test layoutWindowLw_withDisplayCutout_fullscreen_landscape()547 public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { 548 addDisplayCutout(); 549 setRotation(ROTATION_90, true /* includingWindows */); 550 551 mWindow.mAttrs.flags = 552 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 553 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 554 addWindowWithRawInsetsState(mWindow); 555 556 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 557 558 assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 559 } 560 561 @Test layoutWindowLw_withDisplayCutout_floatingInScreen()562 public void layoutWindowLw_withDisplayCutout_floatingInScreen() { 563 addDisplayCutout(); 564 565 mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN; 566 mWindow.mAttrs.setFitInsetsTypes(Type.systemBars() & ~Type.statusBars()); 567 mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; 568 mWindow.mAttrs.width = DISPLAY_WIDTH; 569 mWindow.mAttrs.height = DISPLAY_HEIGHT; 570 addWindowWithRawInsetsState(mWindow); 571 572 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 573 574 assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); 575 assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); 576 } 577 578 @Test layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape()579 public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { 580 addDisplayCutout(); 581 setRotation(ROTATION_90, true /* includingWindows */); 582 583 mWindow.mAttrs.flags = 584 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 585 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 586 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 587 addWindowWithRawInsetsState(mWindow); 588 589 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 590 591 assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); 592 } 593 594 @Test layoutWindowLw_withLongEdgeDisplayCutout()595 public void layoutWindowLw_withLongEdgeDisplayCutout() { 596 addLongEdgeDisplayCutout(); 597 598 mWindow.mAttrs.flags = 599 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 600 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 601 addWindowWithRawInsetsState(mWindow); 602 603 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 604 605 assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 606 assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 607 } 608 609 @Test layoutWindowLw_withLongEdgeDisplayCutout_never()610 public void layoutWindowLw_withLongEdgeDisplayCutout_never() { 611 addLongEdgeDisplayCutout(); 612 613 mWindow.mAttrs.flags = 614 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 615 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 616 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; 617 addWindowWithRawInsetsState(mWindow); 618 619 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 620 621 assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 622 assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 623 } 624 625 @Test layoutWindowLw_withLongEdgeDisplayCutout_shortEdges()626 public void layoutWindowLw_withLongEdgeDisplayCutout_shortEdges() { 627 addLongEdgeDisplayCutout(); 628 629 mWindow.mAttrs.flags = 630 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 631 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 632 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 633 addWindowWithRawInsetsState(mWindow); 634 635 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 636 637 assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 638 assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); 639 } 640 641 @Test layoutWindowLw_withLongEdgeDisplayCutout_always()642 public void layoutWindowLw_withLongEdgeDisplayCutout_always() { 643 addLongEdgeDisplayCutout(); 644 645 mWindow.mAttrs.flags = 646 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 647 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 648 mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 649 addWindowWithRawInsetsState(mWindow); 650 651 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 652 653 assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); 654 assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); 655 } 656 657 @Test layoutWindowLw_withForwardInset_SoftInputAdjustNothing()658 public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() { 659 mWindow.mAttrs.flags = 660 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 661 mWindow.mAttrs.setFitInsetsTypes(0 /* types */); 662 mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; 663 addWindowWithRawInsetsState(mWindow); 664 665 final int forwardedInsetBottom = 50; 666 mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); 667 mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); 668 669 assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); 670 assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); 671 } 672 673 @Test layoutHint_appWindow()674 public void layoutHint_appWindow() { 675 mWindow.mAttrs.setFitInsetsTypes(0); 676 677 final DisplayCutout.ParcelableWrapper outDisplayCutout = 678 new DisplayCutout.ParcelableWrapper(); 679 final InsetsState outState = new InsetsState(); 680 681 mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState, 682 true /* localClient */); 683 684 assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); 685 assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(), 686 is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT))); 687 assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(), 688 is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT))); 689 } 690 691 /** 692 * Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as 693 * the real one. 694 */ 695 @Test testSimulateLayoutDisplay()696 public void testSimulateLayoutDisplay() { 697 assertSimulateLayoutSameDisplayFrames(); 698 setRotation(ROTATION_90, false /* includingWindows */); 699 assertSimulateLayoutSameDisplayFrames(); 700 addDisplayCutout(); 701 assertSimulateLayoutSameDisplayFrames(); 702 addRoundedCorners(); 703 assertSimulateLayoutSameDisplayFrames(); 704 } 705 assertSimulateLayoutSameDisplayFrames()706 private void assertSimulateLayoutSameDisplayFrames() { 707 final String prefix = ""; 708 final InsetsState simulatedInsetsState = new InsetsState(); 709 final DisplayFrames simulatedDisplayFrames = createDisplayFrames(simulatedInsetsState); 710 // Force the display bounds because it is not synced with display frames in policy test. 711 mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted); 712 mDisplayContent.getInsetsStateController().onPostLayout(); 713 mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, 714 new SparseArray<>() /* barContentFrames */); 715 716 final StringWriter realFramesDump = new StringWriter(); 717 mFrames.dump(prefix, new PrintWriter(realFramesDump)); 718 final StringWriter simulatedFramesDump = new StringWriter(); 719 simulatedDisplayFrames.dump(prefix, new PrintWriter(simulatedFramesDump)); 720 721 assertEquals(new ToStringComparatorWrapper<>(realFramesDump), 722 new ToStringComparatorWrapper<>(simulatedFramesDump)); 723 724 final InsetsState realInsetsState = new InsetsState( 725 mDisplayContent.getInsetsStateController().getRawInsetsState()); 726 // Exclude comparing IME insets because currently the simulated layout only focuses on the 727 // insets from status bar and navigation bar. 728 realInsetsState.removeSource(InsetsState.ITYPE_IME); 729 realInsetsState.removeSource(InsetsState.ITYPE_CAPTION_BAR); 730 731 assertEquals(new ToStringComparatorWrapper<>(realInsetsState), 732 new ToStringComparatorWrapper<>(simulatedInsetsState)); 733 } 734 735 @Test testFixedRotationInsetsSourceFrame()736 public void testFixedRotationInsetsSourceFrame() { 737 mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT; 738 mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH; 739 doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent) 740 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord)); 741 mWindow.mAboveInsetsState.set( 742 mDisplayContent.getInsetsStateController().getRawInsetsState()); 743 final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) 744 .getSource(ITYPE_STATUS_BAR).getFrame(); 745 mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord); 746 final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) 747 .getSource(ITYPE_STATUS_BAR).getFrame(); 748 749 assertEquals(DISPLAY_WIDTH, frame.width()); 750 assertEquals(DISPLAY_HEIGHT, rotatedFrame.width()); 751 } 752 753 /** 754 * Asserts that {@code actual} is inset by the given amounts from the full display rect. 755 * 756 * Convenience wrapper for when only the top and bottom inset are non-zero. 757 */ assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom)758 private void assertInsetByTopBottom(Rect actual, int expectedInsetTop, 759 int expectedInsetBottom) { 760 assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); 761 } 762 763 /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, int expectedInsetRight, int expectedInsetBottom)764 private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, 765 int expectedInsetRight, int expectedInsetBottom) { 766 assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, 767 mFrames.mDisplayWidth - expectedInsetRight, 768 mFrames.mDisplayHeight - expectedInsetBottom), actual); 769 } 770 } 771