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.DisplayCutout.NO_CUTOUT; 20 import static android.view.InsetsSource.ID_IME; 21 import static android.view.Surface.ROTATION_0; 22 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 23 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 24 import static android.view.ViewRootImpl.CLIENT_TRANSIENT; 25 import static android.view.WindowInsets.Type.navigationBars; 26 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 27 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 28 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 29 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; 30 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 31 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 32 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 33 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 34 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 37 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 38 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 39 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 41 42 import static org.junit.Assert.assertEquals; 43 import static org.junit.Assert.assertFalse; 44 import static org.junit.Assert.assertNotEquals; 45 import static org.junit.Assert.assertNotNull; 46 import static org.junit.Assert.assertNull; 47 import static org.junit.Assert.assertTrue; 48 import static org.mockito.ArgumentMatchers.anyInt; 49 import static org.mockito.Mockito.doReturn; 50 import static org.mockito.Mockito.spy; 51 import static org.mockito.Mockito.when; 52 53 import android.graphics.Insets; 54 import android.graphics.PixelFormat; 55 import android.graphics.Rect; 56 import android.platform.test.annotations.Presubmit; 57 import android.view.DisplayInfo; 58 import android.view.InsetsFrameProvider; 59 import android.view.InsetsSource; 60 import android.view.InsetsState; 61 import android.view.Surface; 62 import android.view.WindowInsets; 63 import android.view.WindowManager; 64 65 import androidx.test.filters.SmallTest; 66 67 import com.android.window.flags.Flags; 68 69 import org.junit.Assume; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 73 @SmallTest 74 @Presubmit 75 @RunWith(WindowTestRunner.class) 76 public class DisplayPolicyTests extends WindowTestsBase { 77 createOpaqueFullscreen(boolean hasLightNavBar)78 private WindowState createOpaqueFullscreen(boolean hasLightNavBar) { 79 final WindowState win = newWindowBuilder("opaqueFullscreen", TYPE_BASE_APPLICATION).build(); 80 final WindowManager.LayoutParams attrs = win.mAttrs; 81 attrs.width = MATCH_PARENT; 82 attrs.height = MATCH_PARENT; 83 attrs.flags = 84 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 85 attrs.format = PixelFormat.OPAQUE; 86 attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0; 87 return win; 88 } 89 createDreamWindow()90 private WindowState createDreamWindow() { 91 final WindowState win = createDreamWindow("dream", TYPE_BASE_APPLICATION); 92 final WindowManager.LayoutParams attrs = win.mAttrs; 93 attrs.width = MATCH_PARENT; 94 attrs.height = MATCH_PARENT; 95 attrs.flags = 96 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 97 attrs.format = PixelFormat.OPAQUE; 98 return win; 99 } 100 createDimmingDialogWindow(boolean canBeImTarget)101 private WindowState createDimmingDialogWindow(boolean canBeImTarget) { 102 final WindowState win = spy(newWindowBuilder("dimmingDialog", TYPE_APPLICATION).build()); 103 final WindowManager.LayoutParams attrs = win.mAttrs; 104 attrs.width = WRAP_CONTENT; 105 attrs.height = WRAP_CONTENT; 106 attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM); 107 attrs.format = PixelFormat.TRANSLUCENT; 108 when(win.isDimming()).thenReturn(true); 109 return win; 110 } 111 createInputMethodWindow(boolean visible, boolean drawNavBar, boolean hasLightNavBar)112 private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar, 113 boolean hasLightNavBar) { 114 final WindowState win = newWindowBuilder("inputMethod", TYPE_INPUT_METHOD).build(); 115 final WindowManager.LayoutParams attrs = win.mAttrs; 116 attrs.width = MATCH_PARENT; 117 attrs.height = MATCH_PARENT; 118 attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN 119 | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0); 120 attrs.format = PixelFormat.TRANSPARENT; 121 attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0; 122 win.mHasSurface = visible; 123 return win; 124 } 125 126 @Test testChooseNavigationColorWindowLw()127 public void testChooseNavigationColorWindowLw() { 128 final WindowState candidate = createOpaqueFullscreen(false); 129 final WindowState dimmingImTarget = createDimmingDialogWindow(true); 130 final WindowState dimmingNonImTarget = createDimmingDialogWindow(false); 131 132 final WindowState visibleIme = createInputMethodWindow(true, true, false); 133 final WindowState invisibleIme = createInputMethodWindow(false, true, false); 134 final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); 135 136 // If everything is null, return null. 137 assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( 138 null, null, true)); 139 140 // If no IME windows, return candidate window. 141 assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( 142 candidate, null, true)); 143 assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 144 dimmingImTarget, null, true)); 145 assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 146 dimmingNonImTarget, null, true)); 147 148 // If IME is not visible, return candidate window. 149 assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( 150 null, invisibleIme, true)); 151 assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( 152 candidate, invisibleIme, true)); 153 assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 154 dimmingImTarget, invisibleIme, true)); 155 assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 156 dimmingNonImTarget, invisibleIme, true)); 157 158 // If IME is visible, return candidate when the candidate window is not dimming. 159 assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( 160 null, visibleIme, true)); 161 assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( 162 candidate, visibleIme, true)); 163 164 // If IME is visible and the candidate window is dimming, checks whether the dimming window 165 // can be IME tartget or not. 166 assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( 167 dimmingImTarget, visibleIme, true)); 168 assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 169 dimmingNonImTarget, visibleIme, true)); 170 171 // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color 172 // window. 173 assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( 174 null, imeNonDrawNavBar, true)); 175 assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( 176 candidate, imeNonDrawNavBar, true)); 177 assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 178 dimmingImTarget, imeNonDrawNavBar, true)); 179 assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( 180 dimmingNonImTarget, imeNonDrawNavBar, true)); 181 } 182 183 @Test testChooseNavigationBackgroundWindow()184 public void testChooseNavigationBackgroundWindow() { 185 final WindowState drawBarWin = createOpaqueFullscreen(false); 186 final WindowState nonDrawBarWin = createDimmingDialogWindow(true); 187 188 final WindowState visibleIme = createInputMethodWindow(true, true, false); 189 final WindowState invisibleIme = createInputMethodWindow(false, true, false); 190 final WindowState nonDrawBarIme = createInputMethodWindow(true, false, false); 191 192 assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( 193 drawBarWin, null, true)); 194 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 195 null, null, true)); 196 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 197 nonDrawBarWin, null, true)); 198 199 assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( 200 drawBarWin, visibleIme, true)); 201 assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( 202 null, visibleIme, true)); 203 assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( 204 nonDrawBarWin, visibleIme, true)); 205 206 assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( 207 drawBarWin, invisibleIme, true)); 208 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 209 null, invisibleIme, true)); 210 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 211 nonDrawBarWin, invisibleIme, true)); 212 213 assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( 214 drawBarWin, nonDrawBarIme, true)); 215 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 216 null, nonDrawBarIme, true)); 217 assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( 218 nonDrawBarWin, nonDrawBarIme, true)); 219 } 220 221 @SetupWindows(addWindows = W_NAVIGATION_BAR) 222 @Test testUpdateLightNavigationBarLw()223 public void testUpdateLightNavigationBarLw() { 224 DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 225 final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false); 226 final WindowState opaqueLightNavBar = createOpaqueFullscreen(true); 227 228 final WindowState dimming = createDimmingDialogWindow(false); 229 230 final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false); 231 final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true); 232 233 mDisplayContent.setLayoutNeeded(); 234 mDisplayContent.performLayout(true /* initial */, false /* updateImeWindows */); 235 236 final InsetsSource navSource = new InsetsSource( 237 InsetsSource.createId(null, 0, navigationBars()), navigationBars()); 238 navSource.setFrame(mNavBarWindow.getFrame()); 239 opaqueDarkNavBar.mAboveInsetsState.addSource(navSource); 240 opaqueLightNavBar.mAboveInsetsState.addSource(navSource); 241 dimming.mAboveInsetsState.addSource(navSource); 242 imeDrawDarkNavBar.mAboveInsetsState.addSource(navSource); 243 imeDrawLightNavBar.mAboveInsetsState.addSource(navSource); 244 245 // If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed. 246 assertEquals(0, 247 displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null)); 248 249 // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag. 250 assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar)); 251 assertEquals(0, displayPolicy.updateLightNavigationBarLw( 252 APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar)); 253 assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw( 254 0, opaqueLightNavBar)); 255 assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw( 256 APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar)); 257 } 258 259 @SetupWindows(addWindows = { W_ACTIVITY, W_STATUS_BAR }) 260 @Test testComputeTopFullscreenOpaqueWindow()261 public void testComputeTopFullscreenOpaqueWindow() { 262 final WindowManager.LayoutParams attrs = mAppWindow.mAttrs; 263 attrs.x = attrs.y = 0; 264 attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT; 265 final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); 266 policy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs); 267 268 policy.applyPostLayoutPolicyLw( 269 mAppWindow, attrs, null /* attached */, null /* imeTarget */); 270 271 assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow()); 272 } 273 274 @SetupWindows(addWindows = W_NOTIFICATION_SHADE) 275 @Test testVisibleProcessWhileDozing()276 public void testVisibleProcessWhileDozing() { 277 final WindowProcessController wpc = mNotificationShadeWindow.getProcess(); 278 final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); 279 policy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs); 280 281 policy.screenTurnedOff(false /* acquireSleepToken */); 282 policy.setAwake(false); 283 policy.screenTurningOn(null /* screenOnListener */); 284 assertTrue(wpc.isShowingUiWhileDozing()); 285 policy.screenTurnedOff(false /* acquireSleepToken */); 286 assertFalse(wpc.isShowingUiWhileDozing()); 287 288 policy.screenTurningOn(null /* screenOnListener */); 289 assertTrue(wpc.isShowingUiWhileDozing()); 290 policy.setAwake(true); 291 assertFalse(wpc.isShowingUiWhileDozing()); 292 } 293 294 @Test(expected = IllegalArgumentException.class) testMainAppWindowDisallowFitSystemWindowTypes()295 public void testMainAppWindowDisallowFitSystemWindowTypes() { 296 final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); 297 final WindowState activity = createBaseApplicationWindow(); 298 activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 299 300 policy.adjustWindowParamsLw(activity, activity.mAttrs); 301 } 302 createApplicationWindow()303 private WindowState createApplicationWindow() { 304 final WindowState win = newWindowBuilder("Application", TYPE_APPLICATION).build(); 305 final WindowManager.LayoutParams attrs = win.mAttrs; 306 attrs.width = MATCH_PARENT; 307 attrs.height = MATCH_PARENT; 308 attrs.flags = FLAG_SHOW_WHEN_LOCKED | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 309 attrs.format = PixelFormat.OPAQUE; 310 win.mHasSurface = true; 311 return win; 312 } 313 createBaseApplicationWindow()314 private WindowState createBaseApplicationWindow() { 315 final WindowState win = newWindowBuilder("Application", TYPE_BASE_APPLICATION).build(); 316 final WindowManager.LayoutParams attrs = win.mAttrs; 317 attrs.width = MATCH_PARENT; 318 attrs.height = MATCH_PARENT; 319 attrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 320 attrs.format = PixelFormat.OPAQUE; 321 win.mHasSurface = true; 322 return win; 323 } 324 325 @Test testOverlappingWithNavBar()326 public void testOverlappingWithNavBar() { 327 final InsetsSource navSource = new InsetsSource( 328 InsetsSource.createId(null, 0, navigationBars()), navigationBars()); 329 navSource.setFrame(new Rect(100, 200, 200, 300)); 330 testOverlappingWithNavBarType(navSource); 331 } 332 333 @Test testOverlappingWithExtraNavBar()334 public void testOverlappingWithExtraNavBar() { 335 final InsetsSource navSource = new InsetsSource( 336 InsetsSource.createId(null, 1, navigationBars()), navigationBars()); 337 navSource.setFrame(new Rect(100, 200, 200, 300)); 338 testOverlappingWithNavBarType(navSource); 339 } 340 testOverlappingWithNavBarType(InsetsSource navSource)341 private void testOverlappingWithNavBarType(InsetsSource navSource) { 342 final WindowState targetWin = createApplicationWindow(); 343 final WindowFrames winFrame = targetWin.getWindowFrames(); 344 winFrame.mFrame.set(new Rect(100, 100, 200, 200)); 345 targetWin.mAboveInsetsState.addSource(navSource); 346 347 assertFalse("Freeform is overlapping with navigation bar", 348 DisplayPolicy.isOverlappingWithNavBar(targetWin)); 349 350 winFrame.mFrame.set(new Rect(100, 101, 200, 201)); 351 assertTrue("Freeform should be overlapping with navigation bar (bottom)", 352 DisplayPolicy.isOverlappingWithNavBar(targetWin)); 353 354 winFrame.mFrame.set(new Rect(99, 200, 199, 300)); 355 assertTrue("Freeform should be overlapping with navigation bar (right)", 356 DisplayPolicy.isOverlappingWithNavBar(targetWin)); 357 358 winFrame.mFrame.set(new Rect(199, 200, 299, 300)); 359 assertTrue("Freeform should be overlapping with navigation bar (left)", 360 DisplayPolicy.isOverlappingWithNavBar(targetWin)); 361 } 362 363 @Test testSwitchDecorInsets()364 public void testSwitchDecorInsets() { 365 final WindowState win = createApplicationWindow(); 366 final WindowState bar = createNavBarWithProvidedInsets(mDisplayContent); 367 bar.getFrame().set(0, mDisplayContent.mDisplayFrames.mHeight - NAV_BAR_HEIGHT, 368 mDisplayContent.mDisplayFrames.mWidth, mDisplayContent.mDisplayFrames.mHeight); 369 final int insetsId = bar.mAttrs.providedInsets[0].getId(); 370 final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() 371 .getOrCreateSourceProvider(insetsId, bar.mAttrs.providedInsets[0].getType()); 372 provider.setServerVisible(true); 373 provider.updateSourceFrame(bar.getFrame()); 374 375 final InsetsState prevInsetsState = new InsetsState(); 376 prevInsetsState.addSource(new InsetsSource(provider.getSource())); 377 378 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 379 final DisplayInfo info = mDisplayContent.getDisplayInfo(); 380 final int w = info.logicalWidth; 381 final int h = info.logicalHeight; 382 displayPolicy.updateDecorInsetsInfo(); 383 final Rect prevConfigFrame = new Rect(displayPolicy.getDecorInsetsInfo(info.rotation, 384 info.logicalWidth, info.logicalHeight).mConfigFrame); 385 386 displayPolicy.updateCachedDecorInsets(); 387 mDisplayContent.updateBaseDisplayMetrics(w / 2, h / 2, 388 info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi); 389 // There is no previous cache. But the current state will be cached. 390 assertFalse(displayPolicy.shouldKeepCurrentDecorInsets()); 391 392 // Switch to original state. 393 displayPolicy.updateCachedDecorInsets(); 394 mDisplayContent.updateBaseDisplayMetrics(w, h, 395 info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi); 396 assertTrue(displayPolicy.shouldKeepCurrentDecorInsets()); 397 // The current insets are restored from cache directly. 398 assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation, 399 info.logicalWidth, info.logicalHeight).mConfigFrame); 400 // Assume that the InsetsSource in current InsetsState is not updated yet. And it will be 401 // replaced by the one in cache. 402 InsetsState currentInsetsState = new InsetsState(); 403 final InsetsSource prevSource = new InsetsSource(provider.getSource()); 404 prevSource.getFrame().scale(0.5f); 405 currentInsetsState.addSource(prevSource); 406 currentInsetsState = mDisplayContent.getInsetsPolicy().adjustInsetsForWindow( 407 win, currentInsetsState); 408 if (com.android.window.flags.Flags.useCachedInsetsForDisplaySwitch()) { 409 assertEquals(prevInsetsState.peekSource(insetsId), 410 currentInsetsState.peekSource(insetsId)); 411 } 412 413 // If screen is not fully turned on, then the cache should be preserved. 414 displayPolicy.screenTurnedOff(false /* acquireSleepToken */); 415 final TransitionController transitionController = mDisplayContent.mTransitionController; 416 spyOn(transitionController); 417 doReturn(true).when(transitionController).isCollecting(); 418 doReturn(Integer.MAX_VALUE).when(transitionController).getCollectingTransitionId(); 419 // Make CachedDecorInsets.canPreserve return false. 420 displayPolicy.physicalDisplayUpdated(); 421 assertFalse(displayPolicy.shouldKeepCurrentDecorInsets()); 422 displayPolicy.getDecorInsetsInfo(info.rotation, info.logicalWidth, info.logicalHeight) 423 .mConfigFrame.offset(1, 1); 424 // Even if CachedDecorInsets.canPreserve returns false, the cache won't be cleared. 425 displayPolicy.updateDecorInsetsInfo(); 426 // Successful to restore from cache. 427 displayPolicy.updateCachedDecorInsets(); 428 assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation, 429 info.logicalWidth, info.logicalHeight).mConfigFrame); 430 } 431 432 @Test testUpdateDisplayConfigurationByDecor()433 public void testUpdateDisplayConfigurationByDecor() { 434 doReturn(NO_CUTOUT).when(mDisplayContent).calculateDisplayCutoutForRotation(anyInt()); 435 final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent); 436 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 437 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 438 final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp; 439 if (Flags.insetsDecoupledConfiguration()) { 440 // No configuration update when flag enables. 441 assertFalse(displayPolicy.updateDecorInsetsInfo()); 442 assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation, 443 di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom); 444 445 final int barHeight = 2 * NAV_BAR_HEIGHT; 446 navbar.mAttrs.providedInsets[0].setInsetsSize(Insets.of(0, 0, 0, barHeight)); 447 assertFalse(displayPolicy.updateDecorInsetsInfo()); 448 assertEquals(barHeight, displayPolicy.getDecorInsetsInfo(di.rotation, 449 di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom); 450 return; 451 } 452 453 assertTrue(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo()); 454 assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation, 455 di.logicalWidth, di.logicalHeight).mConfigInsets.bottom); 456 mDisplayContent.sendNewConfiguration(); 457 assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp); 458 assertFalse(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo()); 459 460 final WindowState statusBar = createStatusBarWithProvidedInsets(mDisplayContent); 461 if (mWm.mConfigTypes == WindowInsets.Type.navigationBars()) { 462 assertFalse(statusBar.providesDisplayDecorInsets() 463 && displayPolicy.updateDecorInsetsInfo()); 464 assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, 465 di.logicalWidth, di.logicalHeight).mConfigInsets.top); 466 } else { 467 assertTrue(statusBar.providesDisplayDecorInsets() 468 && displayPolicy.updateDecorInsetsInfo()); 469 assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation, 470 di.logicalWidth, di.logicalHeight).mConfigInsets.top); 471 } 472 473 // Flush the pending change (DecorInsets.Info#mNeedUpdate) for the rotation to be tested. 474 displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, di.logicalHeight, di.logicalWidth); 475 // Add a window that provides the same insets in current rotation. But it specifies 476 // different insets in other rotations. 477 final WindowState bar2 = newWindowBuilder("bar2", navbar.mAttrs.type).build(); 478 bar2.mAttrs.providedInsets = new InsetsFrameProvider[] { 479 new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars()) 480 .setInsetsSize(Insets.of(0, 0, 0, NAV_BAR_HEIGHT)) 481 }; 482 bar2.mAttrs.setFitInsetsTypes(0); 483 bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4]; 484 final int doubleHeightFor90 = NAV_BAR_HEIGHT * 2; 485 for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) { 486 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 487 params.setFitInsetsTypes(0); 488 if (i == Surface.ROTATION_90) { 489 params.providedInsets = new InsetsFrameProvider[] { 490 new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars()) 491 .setInsetsSize(Insets.of(0, 0, 0, doubleHeightFor90)) 492 }; 493 } else { 494 params.providedInsets = bar2.mAttrs.providedInsets; 495 } 496 bar2.mAttrs.paramsForRotation[i] = params; 497 } 498 displayPolicy.addWindowLw(bar2, bar2.mAttrs); 499 // Current rotation is 0 and the top insets is still STATUS_BAR_HEIGHT, so no change. 500 assertFalse(displayPolicy.updateDecorInsetsInfo()); 501 // The insets in other rotations should be still updated. 502 assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, 503 di.logicalHeight, di.logicalWidth).mConfigInsets.bottom); 504 // Restore to previous height and the insets can still be updated. 505 bar2.mAttrs.paramsForRotation[Surface.ROTATION_90].providedInsets[0].setInsetsSize( 506 Insets.of(0, 0, 0, NAV_BAR_HEIGHT)); 507 assertFalse(displayPolicy.updateDecorInsetsInfo()); 508 assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, 509 di.logicalHeight, di.logicalWidth).mConfigInsets.bottom); 510 511 navbar.removeIfPossible(); 512 bar2.removeIfPossible(); 513 assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth, 514 di.logicalHeight).mNonDecorInsets.bottom); 515 } 516 517 @SetupWindows(addWindows = W_INPUT_METHOD) 518 @Test testImeInsetsGivenContentFrame()519 public void testImeInsetsGivenContentFrame() { 520 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 521 522 mDisplayContent.setInputMethodWindowLocked(mImeWindow); 523 makeWindowVisible(mImeWindow); 524 mImeWindow.getControllableInsetProvider().setServerVisible(true); 525 526 mImeWindow.mGivenContentInsets.set(0, 10, 0, 0); 527 528 displayPolicy.layoutWindowLw(mImeWindow, null, mDisplayContent.mDisplayFrames); 529 final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); 530 final InsetsSource imeSource = state.peekSource(ID_IME); 531 532 assertNotNull(imeSource); 533 assertFalse(imeSource.getFrame().isEmpty()); 534 assertEquals(mImeWindow.getWindowFrames().mFrame.height() - 10, 535 imeSource.getFrame().height()); 536 } 537 538 @SetupWindows(addWindows = { W_ACTIVITY, W_NAVIGATION_BAR }) 539 @Test testCanSystemBarsBeShownByUser()540 public void testCanSystemBarsBeShownByUser() { 541 Assume.assumeFalse(CLIENT_TRANSIENT); 542 ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true; 543 mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 544 mAppWindow.setRequestedVisibleTypes(0, navigationBars()); 545 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 546 displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); 547 final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider(); 548 navBarProvider.updateControlForTarget(mAppWindow, false, null /* statsToken */); 549 navBarProvider.getSource().setVisible(false); 550 551 displayPolicy.setCanSystemBarsBeShownByUser(false); 552 displayPolicy.requestTransientBars(mNavBarWindow, true); 553 assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars())); 554 555 displayPolicy.setCanSystemBarsBeShownByUser(true); 556 displayPolicy.requestTransientBars(mNavBarWindow, true); 557 assertTrue(mDisplayContent.getInsetsPolicy().isTransient(navigationBars())); 558 } 559 560 @UseTestDisplay(addWindows = { W_NAVIGATION_BAR }) 561 @Test testTransientBarsSuppressedOnDreams()562 public void testTransientBarsSuppressedOnDreams() { 563 Assume.assumeFalse(CLIENT_TRANSIENT); 564 final WindowState win = createDreamWindow(); 565 566 ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true; 567 win.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 568 win.setRequestedVisibleTypes(0, navigationBars()); 569 570 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 571 displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); 572 final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider(); 573 navBarProvider.updateControlForTarget(win, false, null /* statsToken */); 574 navBarProvider.getSource().setVisible(false); 575 576 displayPolicy.setCanSystemBarsBeShownByUser(true); 577 displayPolicy.requestTransientBars(mNavBarWindow, true); 578 579 assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars())); 580 } 581 } 582