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 17 package com.android.server.wm; 18 19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; 23 import static android.os.Build.VERSION_CODES.P; 24 import static android.os.Build.VERSION_CODES.Q; 25 import static android.view.Display.DEFAULT_DISPLAY; 26 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; 27 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; 28 import static android.view.DisplayCutout.fromBoundingRect; 29 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; 30 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 31 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; 32 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 33 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 34 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 39 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 40 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 41 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; 42 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; 45 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; 50 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; 51 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 52 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 53 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 54 import static com.android.server.wm.WindowContainer.POSITION_TOP; 55 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; 56 57 import static org.hamcrest.Matchers.is; 58 import static org.junit.Assert.assertEquals; 59 import static org.junit.Assert.assertFalse; 60 import static org.junit.Assert.assertNotNull; 61 import static org.junit.Assert.assertNull; 62 import static org.junit.Assert.assertThat; 63 import static org.junit.Assert.assertTrue; 64 import static org.mockito.ArgumentMatchers.eq; 65 66 import android.annotation.SuppressLint; 67 import android.app.WindowConfiguration; 68 import android.content.res.Configuration; 69 import android.graphics.Rect; 70 import android.graphics.Region; 71 import android.metrics.LogMaker; 72 import android.os.SystemClock; 73 import android.platform.test.annotations.Presubmit; 74 import android.util.DisplayMetrics; 75 import android.util.MutableBoolean; 76 import android.view.DisplayCutout; 77 import android.view.Gravity; 78 import android.view.ISystemGestureExclusionListener; 79 import android.view.MotionEvent; 80 import android.view.Surface; 81 import android.view.ViewRootImpl; 82 import android.view.test.InsetsModeSession; 83 84 import androidx.test.filters.SmallTest; 85 86 import com.android.dx.mockito.inline.extended.ExtendedMockito; 87 import com.android.internal.logging.MetricsLogger; 88 import com.android.internal.logging.nano.MetricsProto; 89 import com.android.server.wm.utils.WmDisplayCutout; 90 91 import org.junit.Test; 92 import org.mockito.ArgumentCaptor; 93 import org.mockito.Mockito; 94 95 import java.util.ArrayList; 96 import java.util.Arrays; 97 import java.util.Collections; 98 import java.util.LinkedList; 99 import java.util.List; 100 101 /** 102 * Tests for the {@link DisplayContent} class. 103 * 104 * Build/Install/Run: 105 * atest WmTests:DisplayContentTests 106 */ 107 @SmallTest 108 @Presubmit 109 public class DisplayContentTests extends WindowTestsBase { 110 111 @Test testForAllWindows()112 public void testForAllWindows() { 113 final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, 114 mDisplayContent, "exiting app"); 115 final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; 116 // Wait until everything in animation handler get executed to prevent the exiting window 117 // from being removed during WindowSurfacePlacer Traversal. 118 waitUntilHandlersIdle(); 119 120 exitingAppToken.mIsExiting = true; 121 exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken); 122 123 assertForAllWindowsOrder(Arrays.asList( 124 mWallpaperWindow, 125 exitingAppWindow, 126 mChildAppWindowBelow, 127 mAppWindow, 128 mChildAppWindowAbove, 129 mDockedDividerWindow, 130 mStatusBarWindow, 131 mNavBarWindow, 132 mImeWindow, 133 mImeDialogWindow)); 134 } 135 136 @Test testForAllWindows_WithAppImeTarget()137 public void testForAllWindows_WithAppImeTarget() { 138 final WindowState imeAppTarget = 139 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); 140 141 mDisplayContent.mInputMethodTarget = imeAppTarget; 142 143 assertForAllWindowsOrder(Arrays.asList( 144 mWallpaperWindow, 145 mChildAppWindowBelow, 146 mAppWindow, 147 mChildAppWindowAbove, 148 imeAppTarget, 149 mImeWindow, 150 mImeDialogWindow, 151 mDockedDividerWindow, 152 mStatusBarWindow, 153 mNavBarWindow)); 154 } 155 156 @Test testForAllWindows_WithChildWindowImeTarget()157 public void testForAllWindows_WithChildWindowImeTarget() throws Exception { 158 mDisplayContent.mInputMethodTarget = mChildAppWindowAbove; 159 160 assertForAllWindowsOrder(Arrays.asList( 161 mWallpaperWindow, 162 mChildAppWindowBelow, 163 mAppWindow, 164 mChildAppWindowAbove, 165 mImeWindow, 166 mImeDialogWindow, 167 mDockedDividerWindow, 168 mStatusBarWindow, 169 mNavBarWindow)); 170 } 171 172 @Test testForAllWindows_WithStatusBarImeTarget()173 public void testForAllWindows_WithStatusBarImeTarget() throws Exception { 174 mDisplayContent.mInputMethodTarget = mStatusBarWindow; 175 176 assertForAllWindowsOrder(Arrays.asList( 177 mWallpaperWindow, 178 mChildAppWindowBelow, 179 mAppWindow, 180 mChildAppWindowAbove, 181 mDockedDividerWindow, 182 mStatusBarWindow, 183 mImeWindow, 184 mImeDialogWindow, 185 mNavBarWindow)); 186 } 187 188 @Test testForAllWindows_WithInBetweenWindowToken()189 public void testForAllWindows_WithInBetweenWindowToken() { 190 // This window is set-up to be z-ordered between some windows that go in the same token like 191 // the nav bar and status bar. 192 final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION, 193 mDisplayContent, "voiceInteractionWindow"); 194 195 assertForAllWindowsOrder(Arrays.asList( 196 mWallpaperWindow, 197 mChildAppWindowBelow, 198 mAppWindow, 199 mChildAppWindowAbove, 200 mDockedDividerWindow, 201 voiceInteractionWindow, 202 mStatusBarWindow, 203 mNavBarWindow, 204 mImeWindow, 205 mImeDialogWindow)); 206 } 207 208 @Test testComputeImeTarget()209 public void testComputeImeTarget() { 210 // Verify that an app window can be an ime target. 211 final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); 212 appWin.setHasSurface(true); 213 assertTrue(appWin.canBeImeTarget()); 214 WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 215 assertEquals(appWin, imeTarget); 216 appWin.mHidden = false; 217 218 // Verify that an child window can be an ime target. 219 final WindowState childWin = createWindow(appWin, 220 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin"); 221 childWin.setHasSurface(true); 222 assertTrue(childWin.canBeImeTarget()); 223 imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 224 assertEquals(childWin, imeTarget); 225 } 226 227 /** 228 * This tests stack movement between displays and proper stack's, task's and app token's display 229 * container references updates. 230 */ 231 @Test testMoveStackBetweenDisplays()232 public void testMoveStackBetweenDisplays() { 233 // Create a second display. 234 final DisplayContent dc = createNewDisplay(); 235 236 // Add stack with activity. 237 final TaskStack stack = createTaskStackOnDisplay(dc); 238 assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId()); 239 assertEquals(dc, stack.getParent().getParent()); 240 assertEquals(dc, stack.getDisplayContent()); 241 242 final Task task = createTaskInStack(stack, 0 /* userId */); 243 final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( 244 dc); 245 task.addChild(token, 0); 246 assertEquals(dc, task.getDisplayContent()); 247 assertEquals(dc, token.getDisplayContent()); 248 249 // Move stack to first display. 250 mDisplayContent.moveStackToDisplay(stack, true /* onTop */); 251 assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId()); 252 assertEquals(mDisplayContent, stack.getParent().getParent()); 253 assertEquals(mDisplayContent, stack.getDisplayContent()); 254 assertEquals(mDisplayContent, task.getDisplayContent()); 255 assertEquals(mDisplayContent, token.getDisplayContent()); 256 } 257 258 /** 259 * This tests override configuration updates for display content. 260 */ 261 @Test testDisplayOverrideConfigUpdate()262 public void testDisplayOverrideConfigUpdate() { 263 final Configuration currentOverrideConfig = 264 mDisplayContent.getRequestedOverrideConfiguration(); 265 266 // Create new, slightly changed override configuration and apply it to the display. 267 final Configuration newOverrideConfig = new Configuration(currentOverrideConfig); 268 newOverrideConfig.densityDpi += 120; 269 newOverrideConfig.fontScale += 0.3; 270 271 mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent); 272 273 // Check that override config is applied. 274 assertEquals(newOverrideConfig, mDisplayContent.getRequestedOverrideConfiguration()); 275 } 276 277 /** 278 * This tests global configuration updates when default display config is updated. 279 */ 280 @Test testDefaultDisplayOverrideConfigUpdate()281 public void testDefaultDisplayOverrideConfigUpdate() { 282 DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); 283 final Configuration currentConfig = defaultDisplay.getConfiguration(); 284 285 // Create new, slightly changed override configuration and apply it to the display. 286 final Configuration newOverrideConfig = new Configuration(currentConfig); 287 newOverrideConfig.densityDpi += 120; 288 newOverrideConfig.fontScale += 0.3; 289 290 mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay); 291 292 // Check that global configuration is updated, as we've updated default display's config. 293 Configuration globalConfig = mWm.mRoot.getConfiguration(); 294 assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi); 295 assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); 296 297 // Return back to original values. 298 mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay); 299 globalConfig = mWm.mRoot.getConfiguration(); 300 assertEquals(currentConfig.densityDpi, globalConfig.densityDpi); 301 assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); 302 } 303 304 /** 305 * Tests tapping on a stack in different display results in window gaining focus. 306 */ 307 @Test testInputEventBringsCorrectDisplayInFocus()308 public void testInputEventBringsCorrectDisplayInFocus() { 309 DisplayContent dc0 = mWm.getDefaultDisplayContentLocked(); 310 // Create a second display 311 final DisplayContent dc1 = createNewDisplay(); 312 313 // Add stack with activity. 314 final TaskStack stack0 = createTaskStackOnDisplay(dc0); 315 final Task task0 = createTaskInStack(stack0, 0 /* userId */); 316 final WindowTestUtils.TestAppWindowToken token = 317 WindowTestUtils.createTestAppWindowToken(dc0); 318 task0.addChild(token, 0); 319 dc0.configureDisplayPolicy(); 320 assertNotNull(dc0.mTapDetector); 321 322 final TaskStack stack1 = createTaskStackOnDisplay(dc1); 323 final Task task1 = createTaskInStack(stack1, 0 /* userId */); 324 final WindowTestUtils.TestAppWindowToken token1 = 325 WindowTestUtils.createTestAppWindowToken(dc0); 326 task1.addChild(token1, 0); 327 dc1.configureDisplayPolicy(); 328 assertNotNull(dc1.mTapDetector); 329 330 // tap on primary display. 331 tapOnDisplay(dc0); 332 // Check focus is on primary display. 333 assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, 334 dc0.findFocusedWindow()); 335 336 // Tap on secondary display. 337 tapOnDisplay(dc1); 338 // Check focus is on secondary. 339 assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, 340 dc1.findFocusedWindow()); 341 } 342 343 @Test testFocusedWindowMultipleDisplays()344 public void testFocusedWindowMultipleDisplays() { 345 doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q); 346 } 347 348 @Test testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled()349 public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() { 350 doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q); 351 } 352 353 @Test testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp()354 public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() { 355 doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P); 356 } 357 doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, int targetSdk)358 private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, 359 int targetSdk) { 360 mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled; 361 362 // Create a focusable window and check that focus is calculated correctly 363 final WindowState window1 = 364 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1"); 365 window1.mAppToken.mTargetSdk = targetSdk; 366 updateFocusedWindow(); 367 assertTrue(window1.isFocused()); 368 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 369 370 // Check that a new display doesn't affect focus 371 final DisplayContent dc = createNewDisplay(); 372 updateFocusedWindow(); 373 assertTrue(window1.isFocused()); 374 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 375 376 // Add a window to the second display, and it should be focused 377 final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2"); 378 window2.mAppToken.mTargetSdk = targetSdk; 379 updateFocusedWindow(); 380 assertTrue(window2.isFocused()); 381 assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused()); 382 assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 383 384 // Move the first window to top including parents, and make sure focus is updated 385 window1.getParent().positionChildAt(POSITION_TOP, window1, true); 386 updateFocusedWindow(); 387 assertTrue(window1.isFocused()); 388 assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused()); 389 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 390 391 // Make sure top focused display not changed if there is a focused app. 392 window1.mAppToken.hiddenRequested = true; 393 window1.getDisplayContent().setFocusedApp(window1.mAppToken); 394 updateFocusedWindow(); 395 assertTrue(!window1.isFocused()); 396 assertEquals(window1.getDisplayId(), 397 mWm.mRoot.getTopFocusedDisplayContent().getDisplayId()); 398 } 399 400 /** 401 * This tests setting the maximum ui width on a display. 402 */ 403 @Test testMaxUiWidth()404 public void testMaxUiWidth() { 405 // Prevent base display metrics for test from being updated to the value of real display. 406 final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); 407 final int baseWidth = 1440; 408 final int baseHeight = 2560; 409 final int baseDensity = 300; 410 411 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); 412 413 final int maxWidth = 300; 414 final int resultingHeight = (maxWidth * baseHeight) / baseWidth; 415 final int resultingDensity = (maxWidth * baseDensity) / baseWidth; 416 417 displayContent.setMaxUiWidth(maxWidth); 418 verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); 419 420 // Assert setting values again does not change; 421 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); 422 verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); 423 424 final int smallerWidth = 200; 425 final int smallerHeight = 400; 426 final int smallerDensity = 100; 427 428 // Specify smaller dimension, verify that it is honored 429 displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity); 430 verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); 431 432 // Verify that setting the max width to a greater value than the base width has no effect 433 displayContent.setMaxUiWidth(maxWidth); 434 verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); 435 } 436 437 @Test testDisplayCutout_rot0()438 public void testDisplayCutout_rot0() { 439 synchronized (mWm.getWindowManagerLock()) { 440 final DisplayContent dc = createNewDisplay(); 441 dc.mInitialDisplayWidth = 200; 442 dc.mInitialDisplayHeight = 400; 443 Rect r = new Rect(80, 0, 120, 10); 444 final DisplayCutout cutout = new WmDisplayCutout( 445 fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null) 446 .computeSafeInsets(200, 400).getDisplayCutout(); 447 448 dc.mInitialDisplayCutout = cutout; 449 dc.setRotation(Surface.ROTATION_0); 450 dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. 451 452 assertEquals(cutout, dc.getDisplayInfo().displayCutout); 453 } 454 } 455 456 @Test testDisplayCutout_rot90()457 public void testDisplayCutout_rot90() { 458 synchronized (mWm.getWindowManagerLock()) { 459 // Prevent mInitialDisplayCutout from being updated from real display (e.g. null 460 // if the device has no cutout). 461 final DisplayContent dc = createDisplayNoUpdateDisplayInfo(); 462 // Rotation may use real display info to compute bound, so here also uses the 463 // same width and height. 464 final int displayWidth = dc.mInitialDisplayWidth; 465 final int displayHeight = dc.mInitialDisplayHeight; 466 final int cutoutWidth = 40; 467 final int cutoutHeight = 10; 468 final int left = (displayWidth - cutoutWidth) / 2; 469 final int top = 0; 470 final int right = (displayWidth + cutoutWidth) / 2; 471 final int bottom = cutoutHeight; 472 473 final Rect r1 = new Rect(left, top, right, bottom); 474 final DisplayCutout cutout = new WmDisplayCutout( 475 fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), 476 null) 477 .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); 478 479 dc.mInitialDisplayCutout = cutout; 480 dc.setRotation(Surface.ROTATION_90); 481 dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. 482 483 // ----o---------- ------------- 484 // | | | | | 485 // | ------o | o--- 486 // | | | | 487 // | | -> | | 488 // | | ---o 489 // | | | 490 // | | ------------- 491 final Rect r = new Rect(top, left, bottom, right); 492 assertEquals(new WmDisplayCutout( 493 fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null) 494 .computeSafeInsets(displayHeight, displayWidth) 495 .getDisplayCutout(), dc.getDisplayInfo().displayCutout); 496 } 497 } 498 499 @Test testLayoutSeq_assignedDuringLayout()500 public void testLayoutSeq_assignedDuringLayout() { 501 synchronized (mWm.getWindowManagerLock()) { 502 503 final DisplayContent dc = createNewDisplay(); 504 final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 505 506 dc.setLayoutNeeded(); 507 dc.performLayout(true /* initial */, false /* updateImeWindows */); 508 509 assertThat(win.mLayoutSeq, is(dc.mLayoutSeq)); 510 } 511 } 512 513 @Test 514 @SuppressLint("InlinedApi") testOrientationDefinedByKeyguard()515 public void testOrientationDefinedByKeyguard() { 516 final DisplayContent dc = createNewDisplay(); 517 518 // When display content is created its configuration is not yet initialized, which could 519 // cause unnecessary configuration propagation, so initialize it here. 520 final Configuration config = new Configuration(); 521 dc.computeScreenConfiguration(config); 522 dc.onRequestedOverrideConfigurationChanged(config); 523 524 // Create a window that requests landscape orientation. It will define device orientation 525 // by default. 526 final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 527 window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); 528 529 final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard"); 530 keyguard.mHasSurface = true; 531 keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 532 533 assertEquals("Screen orientation must be defined by the app window by default", 534 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); 535 536 keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; 537 assertEquals("Visible keyguard must influence device orientation", 538 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); 539 540 mWm.setKeyguardGoingAway(true); 541 assertEquals("Keyguard that is going away must not influence device orientation", 542 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); 543 } 544 545 @Test testOrientationForAspectRatio()546 public void testOrientationForAspectRatio() { 547 final DisplayContent dc = createNewDisplay(); 548 549 // When display content is created its configuration is not yet initialized, which could 550 // cause unnecessary configuration propagation, so initialize it here. 551 final Configuration config = new Configuration(); 552 dc.computeScreenConfiguration(config); 553 dc.onRequestedOverrideConfigurationChanged(config); 554 555 // Create a window that requests a fixed orientation. It will define device orientation 556 // by default. 557 final WindowState window = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, dc, 558 "window"); 559 window.mHasSurface = true; 560 window.mAttrs.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE; 561 562 // -------------------------------- 563 // Test non-close-to-square display 564 // -------------------------------- 565 dc.mBaseDisplayWidth = 1000; 566 dc.mBaseDisplayHeight = (int) (dc.mBaseDisplayWidth * dc.mCloseToSquareMaxAspectRatio * 2f); 567 dc.configureDisplayPolicy(); 568 569 assertEquals("Screen orientation must be defined by the window by default.", 570 window.mAttrs.screenOrientation, dc.getOrientation()); 571 572 // ---------------------------- 573 // Test close-to-square display 574 // ---------------------------- 575 dc.mBaseDisplayHeight = dc.mBaseDisplayWidth; 576 dc.configureDisplayPolicy(); 577 578 assertEquals("Screen orientation must be SCREEN_ORIENTATION_USER.", 579 SCREEN_ORIENTATION_USER, dc.getOrientation()); 580 } 581 582 @Test testDisableDisplayInfoOverrideFromWindowManager()583 public void testDisableDisplayInfoOverrideFromWindowManager() { 584 final DisplayContent dc = createNewDisplay(); 585 586 assertTrue(dc.mShouldOverrideDisplayConfiguration); 587 mWm.dontOverrideDisplayInfo(dc.getDisplayId()); 588 589 assertFalse(dc.mShouldOverrideDisplayConfiguration); 590 verify(mWm.mDisplayManagerInternal, times(1)) 591 .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null); 592 } 593 594 @Test testClearLastFocusWhenReparentingFocusedWindow()595 public void testClearLastFocusWhenReparentingFocusedWindow() { 596 final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked(); 597 final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, 598 defaultDisplay, "window"); 599 defaultDisplay.mLastFocus = window; 600 mDisplayContent.mCurrentFocus = window; 601 mDisplayContent.reParentWindowToken(window.mToken); 602 603 assertNull(defaultDisplay.mLastFocus); 604 } 605 606 @Test testGetPreferredOptionsPanelGravityFromDifferentDisplays()607 public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() { 608 final DisplayContent portraitDisplay = createNewDisplay(); 609 portraitDisplay.mInitialDisplayHeight = 2000; 610 portraitDisplay.mInitialDisplayWidth = 1000; 611 612 portraitDisplay.setRotation(Surface.ROTATION_0); 613 assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); 614 portraitDisplay.setRotation(Surface.ROTATION_90); 615 assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); 616 617 final DisplayContent landscapeDisplay = createNewDisplay(); 618 landscapeDisplay.mInitialDisplayHeight = 1000; 619 landscapeDisplay.mInitialDisplayWidth = 2000; 620 621 landscapeDisplay.setRotation(Surface.ROTATION_0); 622 assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); 623 landscapeDisplay.setRotation(Surface.ROTATION_90); 624 assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); 625 } 626 627 @Test testInputMethodTargetUpdateWhenSwitchingOnDisplays()628 public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() { 629 final DisplayContent newDisplay = createNewDisplay(); 630 631 final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); 632 final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); 633 appWin.setHasSurface(true); 634 appWin1.setHasSurface(true); 635 636 // Set current input method window on default display, make sure the input method target 637 // is appWin & null on the other display. 638 mDisplayContent.setInputMethodWindowLocked(mImeWindow); 639 newDisplay.setInputMethodWindowLocked(null); 640 assertTrue("appWin should be IME target window", 641 appWin.equals(mDisplayContent.mInputMethodTarget)); 642 assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget); 643 644 // Switch input method window on new display & make sure the input method target also 645 // switched as expected. 646 newDisplay.setInputMethodWindowLocked(mImeWindow); 647 mDisplayContent.setInputMethodWindowLocked(null); 648 assertTrue("appWin1 should be IME target window", 649 appWin1.equals(newDisplay.mInputMethodTarget)); 650 assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget); 651 } 652 653 @Test testOnDescendantOrientationRequestChanged()654 public void testOnDescendantOrientationRequestChanged() { 655 final DisplayContent dc = createNewDisplay(); 656 mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); 657 final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE 658 ? SCREEN_ORIENTATION_PORTRAIT 659 : SCREEN_ORIENTATION_LANDSCAPE; 660 661 final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 662 window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); 663 window.mAppToken.setOrientation(newOrientation); 664 665 ActivityRecord activityRecord = mock(ActivityRecord.class); 666 667 assertTrue("Display should rotate to handle orientation request by default.", 668 dc.onDescendantOrientationChanged(window.mToken.token, activityRecord)); 669 670 final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class); 671 verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(), 672 same(activityRecord), anyBoolean(), eq(dc.getDisplayId())); 673 final Configuration newDisplayConfig = captor.getValue(); 674 assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation); 675 } 676 677 @Test testOnDescendantOrientationRequestChanged_FrozenToUserRotation()678 public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { 679 final DisplayContent dc = createNewDisplay(); 680 dc.getDisplayRotation().setFixedToUserRotation( 681 DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED); 682 mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); 683 final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE 684 ? SCREEN_ORIENTATION_PORTRAIT 685 : SCREEN_ORIENTATION_LANDSCAPE; 686 687 final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 688 window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS); 689 window.mAppToken.setOrientation(newOrientation); 690 691 ActivityRecord activityRecord = mock(ActivityRecord.class); 692 693 assertFalse("Display shouldn't rotate to handle orientation request if fixed to" 694 + " user rotation.", 695 dc.onDescendantOrientationChanged(window.mToken.token, activityRecord)); 696 verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(), 697 eq(activityRecord), anyBoolean(), eq(dc.getDisplayId())); 698 } 699 700 @Test testComputeImeParent_app()701 public void testComputeImeParent_app() throws Exception { 702 try (final InsetsModeSession session = 703 new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { 704 final DisplayContent dc = createNewDisplay(); 705 dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); 706 assertEquals(dc.mInputMethodTarget.mAppToken.getSurfaceControl(), 707 dc.computeImeParent()); 708 } 709 } 710 711 @Test testComputeImeParent_app_notFullscreen()712 public void testComputeImeParent_app_notFullscreen() throws Exception { 713 try (final InsetsModeSession session = 714 new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { 715 final DisplayContent dc = createNewDisplay(); 716 dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app"); 717 dc.mInputMethodTarget.setWindowingMode( 718 WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); 719 assertEquals(dc.getWindowingLayer(), dc.computeImeParent()); 720 } 721 } 722 723 @Test testComputeImeParent_app_notMatchParentBounds()724 public void testComputeImeParent_app_notMatchParentBounds() { 725 spyOn(mAppWindow.mAppToken); 726 doReturn(false).when(mAppWindow.mAppToken).matchParentBounds(); 727 mDisplayContent.mInputMethodTarget = mAppWindow; 728 // The surface parent of IME should be the display instead of app window. 729 assertEquals(mDisplayContent.getWindowingLayer(), mDisplayContent.computeImeParent()); 730 } 731 732 @Test testComputeImeParent_noApp()733 public void testComputeImeParent_noApp() throws Exception { 734 try (final InsetsModeSession session = 735 new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { 736 final DisplayContent dc = createNewDisplay(); 737 dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 738 assertEquals(dc.getWindowingLayer(), dc.computeImeParent()); 739 } 740 } 741 742 @Test testUpdateSystemGestureExclusion()743 public void testUpdateSystemGestureExclusion() throws Exception { 744 final DisplayContent dc = createNewDisplay(); 745 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 746 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 747 win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); 748 749 dc.setLayoutNeeded(); 750 dc.performLayout(true /* initial */, false /* updateImeWindows */); 751 752 win.setHasSurface(true); 753 dc.updateSystemGestureExclusion(); 754 755 final MutableBoolean invoked = new MutableBoolean(false); 756 final ISystemGestureExclusionListener.Stub verifier = 757 new ISystemGestureExclusionListener.Stub() { 758 @Override 759 public void onSystemGestureExclusionChanged(int displayId, Region actual, 760 Region unrestricted) { 761 Region expected = Region.obtain(); 762 expected.set(10, 20, 30, 40); 763 assertEquals(expected, actual); 764 invoked.value = true; 765 } 766 }; 767 try { 768 dc.registerSystemGestureExclusionListener(verifier); 769 } finally { 770 dc.unregisterSystemGestureExclusionListener(verifier); 771 } 772 assertTrue("SystemGestureExclusionListener was not invoked", invoked.value); 773 } 774 775 @Test testCalculateSystemGestureExclusion()776 public void testCalculateSystemGestureExclusion() throws Exception { 777 final DisplayContent dc = createNewDisplay(); 778 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 779 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 780 win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); 781 782 final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2"); 783 win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 784 win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50))); 785 786 dc.setLayoutNeeded(); 787 dc.performLayout(true /* initial */, false /* updateImeWindows */); 788 789 win.setHasSurface(true); 790 win2.setHasSurface(true); 791 792 final Region expected = Region.obtain(); 793 expected.set(20, 30, 40, 50); 794 assertEquals(expected, calculateSystemGestureExclusion(dc)); 795 } 796 calculateSystemGestureExclusion(DisplayContent dc)797 private Region calculateSystemGestureExclusion(DisplayContent dc) { 798 Region out = Region.obtain(); 799 Region unrestricted = Region.obtain(); 800 dc.calculateSystemGestureExclusion(out, unrestricted); 801 return out; 802 } 803 804 @Test testCalculateSystemGestureExclusion_modal()805 public void testCalculateSystemGestureExclusion_modal() throws Exception { 806 final DisplayContent dc = createNewDisplay(); 807 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base"); 808 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 809 win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000))); 810 811 final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal"); 812 win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 813 win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; 814 win2.getAttrs().width = 10; 815 win2.getAttrs().height = 10; 816 win2.setSystemGestureExclusion(Collections.emptyList()); 817 818 dc.setLayoutNeeded(); 819 dc.performLayout(true /* initial */, false /* updateImeWindows */); 820 821 win.setHasSurface(true); 822 win2.setHasSurface(true); 823 824 final Region expected = Region.obtain(); 825 assertEquals(expected, calculateSystemGestureExclusion(dc)); 826 } 827 828 @Test testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow()829 public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception { 830 synchronized (mWm.mGlobalLock) { 831 mWm.mSystemGestureExcludedByPreQStickyImmersive = true; 832 833 final DisplayContent dc = createNewDisplay(); 834 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 835 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 836 win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 837 win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; 838 win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility = 839 SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION 840 | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; 841 win.mAppToken.mTargetSdk = P; 842 843 dc.setLayoutNeeded(); 844 dc.performLayout(true /* initial */, false /* updateImeWindows */); 845 846 win.setHasSurface(true); 847 848 final Region expected = Region.obtain(); 849 expected.set(dc.getBounds()); 850 assertEquals(expected, calculateSystemGestureExclusion(dc)); 851 852 win.setHasSurface(false); 853 } 854 } 855 856 @Test testOrientationChangeLogging()857 public void testOrientationChangeLogging() { 858 MetricsLogger mockLogger = mock(MetricsLogger.class); 859 Configuration oldConfig = new Configuration(); 860 oldConfig.orientation = Configuration.ORIENTATION_LANDSCAPE; 861 862 Configuration newConfig = new Configuration(); 863 newConfig.orientation = Configuration.ORIENTATION_PORTRAIT; 864 final DisplayContent displayContent = spy(createNewDisplay()); 865 Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger(); 866 Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration(); 867 868 displayContent.onConfigurationChanged(newConfig); 869 870 ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); 871 verify(mockLogger).write(logMakerCaptor.capture()); 872 assertThat(logMakerCaptor.getValue().getCategory(), 873 is(MetricsProto.MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)); 874 assertThat(logMakerCaptor.getValue().getSubtype(), 875 is(Configuration.ORIENTATION_PORTRAIT)); 876 } 877 isOptionsPanelAtRight(int displayId)878 private boolean isOptionsPanelAtRight(int displayId) { 879 return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; 880 } 881 verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity)882 private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, 883 int expectedBaseHeight, int expectedBaseDensity) { 884 assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth); 885 assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight); 886 assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity); 887 } 888 updateFocusedWindow()889 private void updateFocusedWindow() { 890 synchronized (mWm.mGlobalLock) { 891 mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false); 892 } 893 } 894 895 /** 896 * Create DisplayContent that does not update display base/initial values from device to keep 897 * the values set by test. 898 */ createDisplayNoUpdateDisplayInfo()899 private DisplayContent createDisplayNoUpdateDisplayInfo() { 900 final DisplayContent displayContent = spy(createNewDisplay()); 901 doNothing().when(displayContent).updateDisplayInfo(); 902 return displayContent; 903 } 904 assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop)905 private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) { 906 final LinkedList<WindowState> actualWindows = new LinkedList<>(); 907 908 // Test forward traversal. 909 mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */); 910 assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop)); 911 912 actualWindows.clear(); 913 914 // Test backward traversal. 915 mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */); 916 assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); 917 } 918 reverseList(List<WindowState> list)919 private static List<WindowState> reverseList(List<WindowState> list) { 920 final ArrayList<WindowState> result = new ArrayList<>(list); 921 Collections.reverse(result); 922 return result; 923 } 924 tapOnDisplay(final DisplayContent dc)925 private void tapOnDisplay(final DisplayContent dc) { 926 final DisplayMetrics dm = dc.getDisplayMetrics(); 927 final float x = dm.widthPixels / 2; 928 final float y = dm.heightPixels / 2; 929 final long downTime = SystemClock.uptimeMillis(); 930 final long eventTime = SystemClock.uptimeMillis() + 100; 931 // sending ACTION_DOWN 932 final MotionEvent downEvent = MotionEvent.obtain( 933 downTime, 934 downTime, 935 MotionEvent.ACTION_DOWN, 936 x, 937 y, 938 0 /*metaState*/); 939 downEvent.setDisplayId(dc.getDisplayId()); 940 dc.mTapDetector.onPointerEvent(downEvent); 941 942 // sending ACTION_UP 943 final MotionEvent upEvent = MotionEvent.obtain( 944 downTime, 945 eventTime, 946 MotionEvent.ACTION_UP, 947 x, 948 y, 949 0 /*metaState*/); 950 upEvent.setDisplayId(dc.getDisplayId()); 951 dc.mTapDetector.onPointerEvent(upEvent); 952 } 953 } 954