1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 25 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 26 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 27 import static android.server.wm.StateLogger.logE; 28 import static android.server.wm.WindowManagerState.STATE_RESUMED; 29 import static android.server.wm.WindowManagerState.dpToPx; 30 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY; 31 import static android.server.wm.app.Components.DIALOG_WHEN_LARGE_ACTIVITY; 32 import static android.server.wm.app.Components.LANDSCAPE_ORIENTATION_ACTIVITY; 33 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 34 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO; 35 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE; 36 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE; 37 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO; 38 import static android.server.wm.app.Components.NIGHT_MODE_ACTIVITY; 39 import static android.server.wm.app.Components.PORTRAIT_ORIENTATION_ACTIVITY; 40 import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY; 41 import static android.server.wm.app.Components.TEST_ACTIVITY; 42 import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY; 43 import static android.view.Surface.ROTATION_0; 44 import static android.view.Surface.ROTATION_180; 45 import static android.view.Surface.ROTATION_270; 46 import static android.view.Surface.ROTATION_90; 47 48 import static org.hamcrest.MatcherAssert.assertThat; 49 import static org.hamcrest.Matchers.lessThan; 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertFalse; 52 import static org.junit.Assert.assertNotEquals; 53 import static org.junit.Assert.assertTrue; 54 import static org.junit.Assert.fail; 55 import static org.junit.Assume.assumeFalse; 56 import static org.junit.Assume.assumeTrue; 57 58 import android.app.Activity; 59 import android.content.ComponentName; 60 import android.content.Context; 61 import android.graphics.Point; 62 import android.graphics.Rect; 63 import android.hardware.display.DisplayManager; 64 import android.os.Bundle; 65 import android.platform.test.annotations.Presubmit; 66 import android.server.wm.CommandSession.ActivitySession; 67 import android.server.wm.CommandSession.ActivitySessionClient; 68 import android.server.wm.CommandSession.ConfigInfo; 69 import android.server.wm.CommandSession.SizeInfo; 70 import android.server.wm.TestJournalProvider.TestJournalContainer; 71 import android.view.Display; 72 import android.window.WindowContainerTransaction; 73 74 import org.junit.Test; 75 76 import java.util.function.Function; 77 78 /** 79 * Build/Install/Run: 80 * atest CtsWindowManagerDeviceTestCases:AppConfigurationTests 81 */ 82 @Presubmit 83 public class AppConfigurationTests extends MultiDisplayTestBase { 84 85 private static final int SMALL_WIDTH_DP = 426; 86 private static final int SMALL_HEIGHT_DP = 320; 87 88 /** 89 * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity 90 * has an updated size when the Activity is resized from fullscreen to docked state. 91 * 92 * The Activity handles configuration changes, so it will not be restarted between resizes. 93 * On Configuration changes, the Activity logs the Display size and Configuration width 94 * and heights. The values reported in fullscreen should be larger than those reported in 95 * docked state. 96 */ 97 @Test testConfigurationUpdatesWhenResizedFromFullscreen()98 public void testConfigurationUpdatesWhenResizedFromFullscreen() { 99 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 100 101 separateTestJournal(); 102 launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 103 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 104 105 separateTestJournal(); 106 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 107 final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 108 109 assertSizesAreSane(fullscreenSizes, dockedSizes); 110 } 111 112 /** 113 * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing 114 * from docked state to fullscreen (reverse). 115 */ 116 @Test testConfigurationUpdatesWhenResizedFromDockedStack()117 public void testConfigurationUpdatesWhenResizedFromDockedStack() { 118 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 119 120 separateTestJournal(); 121 launchActivitiesInSplitScreen( 122 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 123 .setWindowingMode(WINDOWING_MODE_FULLSCREEN), 124 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) 125 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 126 127 final SizeInfo dockedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 128 129 separateTestJournal(); 130 dismissSplitScreen(true /* primaryOnTop */); 131 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 132 133 assertSizesAreSane(fullscreenSizes, dockedSizes); 134 } 135 136 /** 137 * Tests whether the Display sizes change when rotating the device. 138 */ 139 @Test testConfigurationUpdatesWhenRotatingWhileFullscreen()140 public void testConfigurationUpdatesWhenRotatingWhileFullscreen() { 141 assumeTrue("Skipping test: no rotation support", supportsRotation()); 142 143 final RotationSession rotationSession = createManagedRotationSession(); 144 rotationSession.set(ROTATION_0); 145 146 separateTestJournal(); 147 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 148 resizeableActivityClient.startActivity(getLaunchActivityBuilder() 149 .setUseInstrumentation() 150 .setTargetActivity(RESIZEABLE_ACTIVITY) 151 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 152 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 153 154 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 155 } 156 157 /** 158 * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity 159 * is in the docked stack. 160 */ 161 @Test testConfigurationUpdatesWhenRotatingWhileDocked()162 public void testConfigurationUpdatesWhenRotatingWhileDocked() { 163 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 164 165 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 166 final RotationSession rotationSession = createManagedRotationSession(); 167 rotationSession.set(ROTATION_0); 168 169 separateTestJournal(); 170 // Launch our own activity to side in case Recents (or other activity to side) doesn't 171 // support rotation. 172 launchActivitiesInSplitScreen( 173 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 174 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 175 // Launch target activity in docked stack. 176 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 177 .setActivitySessionClient(resizeableActivityClient).execute(); 178 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 179 180 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 181 } 182 183 /** 184 * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity 185 * is launched to side from docked stack. 186 */ 187 @Test testConfigurationUpdatesWhenRotatingToSideFromDocked()188 public void testConfigurationUpdatesWhenRotatingToSideFromDocked() { 189 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 190 191 final ActivitySessionClient resizeableActivityClient = createManagedActivityClientSession(); 192 final RotationSession rotationSession = createManagedRotationSession(); 193 rotationSession.set(ROTATION_0); 194 195 separateTestJournal(); 196 launchActivitiesInSplitScreen( 197 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY), 198 getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY) 199 .setActivitySessionClient(resizeableActivityClient)); 200 final SizeInfo initialSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 201 202 rotateAndCheckSizes(rotationSession, resizeableActivityClient, initialSizes); 203 } 204 rotateAndCheckSizes(RotationSession rotationSession, ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes)205 private void rotateAndCheckSizes(RotationSession rotationSession, 206 ActivitySessionClient noRelaunchActivityClient, SizeInfo prevSizes) { 207 final ActivitySession activitySession = noRelaunchActivityClient.getLastStartedSession(); 208 final ComponentName activityName = activitySession.getName(); 209 final WindowManagerState.Task task = mWmState.getTaskByActivity(activityName); 210 final int displayId = mWmState.getRootTask(task.mRootTaskId).mDisplayId; 211 212 assumeTrue(supportsLockedUserRotation(rotationSession, displayId)); 213 214 final boolean isCloseToSquareDisplay = isCloseToSquareDisplay(); 215 final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 }; 216 for (final int rotation : rotations) { 217 separateTestJournal(); 218 rotationSession.set(rotation); 219 final int newDeviceRotation = getDeviceRotation(displayId); 220 if (newDeviceRotation == INVALID_DEVICE_ROTATION) { 221 logE("Got an invalid device rotation value. " 222 + "Continuing the test despite of that, but it is likely to fail."); 223 } 224 final boolean expectConfigChange = task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN 225 && !isCloseToSquareDisplay; 226 if (expectConfigChange) { 227 assertActivityLifecycle(activityName, false /* relaunch */); 228 } 229 final SizeInfo rotatedSizes = activitySession.getConfigInfo().sizeInfo; 230 assertSizesRotate(prevSizes, rotatedSizes, 231 // Skip orientation checks if we are not in fullscreen mode, or when the display 232 // is close to square because the app config orientation may always be landscape 233 // excluding the system insets. 234 !expectConfigChange /* skipOrientationCheck */); 235 prevSizes = rotatedSizes; 236 } 237 } 238 239 /** 240 * Tests when activity moved from fullscreen stack to docked and back. Activity will be 241 * relaunched twice and it should have same config as initial one. 242 */ 243 @Test testSameConfigurationFullSplitFullRelaunch()244 public void testSameConfigurationFullSplitFullRelaunch() { 245 moveActivityFullSplitFull(true /* relaunch */); 246 } 247 248 /** 249 * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch. 250 */ 251 @Test testSameConfigurationFullSplitFullNoRelaunch()252 public void testSameConfigurationFullSplitFullNoRelaunch() { 253 moveActivityFullSplitFull(false /* relaunch */); 254 } 255 256 /** 257 * Launches activity in fullscreen task, moves to docked task and back to fullscreen task. 258 * Asserts that initial and final reported sizes in fullscreen task are the same. 259 */ moveActivityFullSplitFull(boolean relaunch)260 private void moveActivityFullSplitFull(boolean relaunch) { 261 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 262 263 final ComponentName activityName = relaunch ? TEST_ACTIVITY : RESIZEABLE_ACTIVITY; 264 // Launch to fullscreen task and record size. 265 separateTestJournal(); 266 launchActivity(activityName, WINDOWING_MODE_FULLSCREEN); 267 final SizeInfo initialFullscreenSizes = getLastReportedSizesForActivity(activityName); 268 269 // Ensure the orientation configuration is different while moving the activity into split 270 // primary task later if we expected activity to be launched. 271 if (relaunch) { 272 mTaskOrganizer.registerOrganizerIfNeeded(); 273 Rect primaryTaskBounds = mTaskOrganizer.getPrimaryTaskBounds(); 274 if (initialFullscreenSizes.displayHeight > initialFullscreenSizes.displayWidth) { 275 primaryTaskBounds.bottom = primaryTaskBounds.width() / 2; 276 } else { 277 primaryTaskBounds.right = primaryTaskBounds.height() / 2; 278 } 279 mTaskOrganizer.setRootPrimaryTaskBounds(primaryTaskBounds); 280 } 281 282 // Move the task to the primary split task. 283 separateTestJournal(); 284 putActivityInPrimarySplit(activityName); 285 // Currently launchActivityInPrimarySplit launches the target activity and then move it 286 // to split task, so it requires waiting of lifecycle to get the stable initial size. 287 if (relaunch) { 288 assertActivityLifecycle(activityName, true /* relaunch */); 289 } else { 290 // The lifecycle callbacks contain the initial launch event so only wait for 291 // multi-window mode changed. 292 waitForOnMultiWindowModeChanged(activityName); 293 } 294 final SizeInfo dockedSizes = getLastReportedSizesForActivity(activityName); 295 assertSizesAreSane(initialFullscreenSizes, dockedSizes); 296 final boolean orientationChanged = 297 initialFullscreenSizes.orientation != dockedSizes.orientation; 298 299 separateTestJournal(); 300 // Restore to fullscreen. 301 final int activityTaskId = mWmState.getTaskByActivity(activityName).mTaskId; 302 final WindowContainerTransaction wct = new WindowContainerTransaction() 303 .setWindowingMode(mTaskOrganizer.getTaskInfo(activityTaskId).getToken(), 304 WINDOWING_MODE_FULLSCREEN); 305 mTaskOrganizer.dismissSplitScreen(wct, false /* primaryOnTop */); 306 // Home task could be on top since it was the top-most task while in split-screen mode 307 // (dock task was minimized), start the activity again to ensure the activity is at 308 // foreground. 309 launchActivity(activityName, WINDOWING_MODE_FULLSCREEN); 310 if (relaunch && !orientationChanged) { 311 // If there is no orientation changes while moving the non-resizeable activity out of 312 // the split, the Activity won't be relaunched because size changes won't cross the 313 // size config buckets. So, there won't be any lifecycle changes. 314 waitForOnMultiWindowModeChanged(activityName); 315 } else { 316 assertActivityLifecycle(activityName, relaunch); 317 } 318 319 // It must report same size as original one after split-screen dismissed. 320 final SizeInfo finalFullscreenSizes = getLastReportedSizesForActivity(activityName); 321 assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes); 322 } 323 324 /** 325 * Tests that an activity with the DialogWhenLarge theme can transform properly when in split 326 * screen. 327 */ 328 @Test testDialogWhenLargeSplitSmall()329 public void testDialogWhenLargeSplitSmall() { 330 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 331 332 launchActivitiesInSplitScreen( 333 getLaunchActivityBuilder().setTargetActivity(DIALOG_WHEN_LARGE_ACTIVITY), 334 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 335 int displayId = mWmState.getDisplayByActivity(DIALOG_WHEN_LARGE_ACTIVITY); 336 final WindowManagerState.DisplayContent display = mWmState.getDisplay(displayId); 337 final int density = display.getDpi(); 338 final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density); 339 final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density); 340 341 mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, smallWidthPx, smallHeightPx)); 342 mWmState.waitForValidState( 343 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY) 344 .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) 345 .setActivityType(ACTIVITY_TYPE_STANDARD) 346 .build()); 347 } 348 349 /** 350 * Test that device handles consequent requested orientations and displays the activities. 351 */ 352 @Test testFullscreenAppOrientationRequests()353 public void testFullscreenAppOrientationRequests() { 354 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 355 356 separateTestJournal(); 357 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 358 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 359 SizeInfo reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 360 assertEquals("portrait activity should be in portrait", 361 ORIENTATION_PORTRAIT, reportedSizes.orientation); 362 assertTrue("portrait activity should have height >= width", 363 reportedSizes.heightDp >= reportedSizes.widthDp); 364 separateTestJournal(); 365 366 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 367 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 368 reportedSizes = getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 369 assertEquals("landscape activity should be in landscape", 370 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 371 assertTrue("landscape activity should have height < width", 372 reportedSizes.heightDp < reportedSizes.widthDp); 373 separateTestJournal(); 374 375 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 376 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 377 reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 378 assertEquals("portrait activity should be in portrait", 379 ORIENTATION_PORTRAIT, reportedSizes.orientation); 380 } 381 382 @Test testTranslucentAppOrientationRequests()383 public void testTranslucentAppOrientationRequests() { 384 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 385 disableIgnoreOrientationRequest(); 386 387 separateTestJournal(); 388 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 389 final SizeInfo initialReportedSizes = 390 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY); 391 assertEquals("portrait activity should be in portrait", 392 ORIENTATION_PORTRAIT, initialReportedSizes.orientation); 393 assertTrue("portrait activity should have height >= width", 394 initialReportedSizes.heightDp >= initialReportedSizes.widthDp); 395 assumeFalse("Skipping test: device is fixed to user rotation", 396 mWmState.isFixedToUserRotation()); 397 398 separateTestJournal(); 399 400 launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 401 assumeNotIgnoringOrientation(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 402 assertEquals("Legacy translucent activity requested landscape orientation", 403 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation()); 404 405 // TODO(b/36897968): uncomment once we can suppress unsupported configurations 406 // final ReportedSizes updatedReportedSizes = 407 // getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator); 408 // assertEquals("portrait activity should not have moved from portrait", 409 // 1 /* portrait */, updatedReportedSizes.orientation); 410 } 411 412 /** 413 * Test that device handles consequent requested orientations and will not report a config 414 * change to an invisible activity. 415 */ 416 @Test testAppOrientationRequestConfigChanges()417 public void testAppOrientationRequestConfigChanges() { 418 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 419 420 separateTestJournal(); 421 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 422 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 423 424 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 425 1 /* create */, 1 /* start */, 1 /* resume */, 426 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */); 427 428 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 429 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 430 431 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 432 1 /* create */, 1 /* start */, 1 /* resume */, 433 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 434 assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, 435 1 /* create */, 1 /* start */, 1 /* resume */, 436 0 /* pause */, 0 /* stop */, 0 /* destroy */, 0 /* config */); 437 438 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 439 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 440 441 assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, 442 2 /* create */, 2 /* start */, 2 /* resume */, 443 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 444 assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, 445 1 /* create */, 1 /* start */, 1 /* resume */, 446 1 /* pause */, 1 /* stop */, 0 /* destroy */, 0 /* config */); 447 } 448 449 /** 450 * Test that device orientation is restored when an activity that requests it is no longer 451 * visible. 452 * 453 * TODO(b/139936670, b/112688380): This test case fails on some vendor devices which has 454 * rotation sensing optimization. So this is listed in cts-known-failures.xml. 455 */ 456 @Test testAppOrientationRequestConfigClears()457 public void testAppOrientationRequestConfigClears() { 458 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 459 460 separateTestJournal(); 461 launchActivity(TEST_ACTIVITY); 462 mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */); 463 final SizeInfo initialReportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY); 464 final int initialOrientation = initialReportedSizes.orientation; 465 466 // Launch an activity that requests different orientation and check that it will be applied 467 final boolean launchingPortrait; 468 if (initialOrientation == ORIENTATION_LANDSCAPE) { 469 launchingPortrait = true; 470 } else if (initialOrientation == ORIENTATION_PORTRAIT) { 471 launchingPortrait = false; 472 } else { 473 fail("Unexpected orientation value: " + initialOrientation); 474 return; 475 } 476 final ComponentName differentOrientationActivity = launchingPortrait 477 ? PORTRAIT_ORIENTATION_ACTIVITY : LANDSCAPE_ORIENTATION_ACTIVITY; 478 separateTestJournal(); 479 launchActivity(differentOrientationActivity); 480 mWmState.assertVisibility(differentOrientationActivity, true /* visible */); 481 final SizeInfo rotatedReportedSizes = 482 getLastReportedSizesForActivity(differentOrientationActivity); 483 assertEquals("Applied orientation must correspond to activity request", 484 launchingPortrait ? 1 : 2, rotatedReportedSizes.orientation); 485 486 // Launch another activity on top and check that its orientation is not affected by previous 487 // activity. 488 separateTestJournal(); 489 launchActivity(RESIZEABLE_ACTIVITY); 490 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 491 final SizeInfo finalReportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 492 assertEquals("Applied orientation must not be influenced by previously visible activity", 493 initialOrientation, finalReportedSizes.orientation); 494 } 495 496 @Test testRotatedInfoWithFixedRotationTransform()497 public void testRotatedInfoWithFixedRotationTransform() { 498 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 499 500 // Start a portrait activity first to ensure that the orientation will change. 501 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY); 502 mWmState.waitForDisplayOrientation(ORIENTATION_PORTRAIT); 503 final int prevRotation = mWmState.getRotation(); 504 505 getLaunchActivityBuilder() 506 .setUseInstrumentation() 507 .setTargetActivity(LANDSCAPE_ORIENTATION_ACTIVITY) 508 // Request the info from onCreate because at that moment the real display hasn't 509 // rotated but the activity is rotated. 510 .setIntentExtra(bundle -> bundle.putBoolean(EXTRA_CONFIG_INFO_IN_ON_CREATE, true)) 511 .execute(); 512 mWmState.waitForDisplayOrientation(ORIENTATION_LANDSCAPE); 513 514 final SizeInfo reportedSizes = 515 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 516 final Bundle extras = TestJournalContainer.get(LANDSCAPE_ORIENTATION_ACTIVITY).extras; 517 final ConfigInfo appConfigInfo = extras.getParcelable(EXTRA_APP_CONFIG_INFO); 518 final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE); 519 final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE); 520 final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo; 521 final ConfigInfo globalConfigInfo = 522 extras.getParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO); 523 final SizeInfo globalSizeInfo = globalConfigInfo.sizeInfo; 524 525 assertEquals("The last reported size should be the same as the one from onCreate", 526 reportedSizes, onCreateConfigInfo.sizeInfo); 527 528 final WindowManagerState.DisplayContent dc = mWmState.getDisplay(Display.DEFAULT_DISPLAY); 529 final Point realDisplaySize = 530 new Point(dc.getDisplayRect().width(), dc.getDisplayRect().height()); 531 final int currentRotation = mWmState.getRotation(); 532 // Some devices may launch the activity in a letterboxed area so the display won't rotate. 533 final boolean displayRotationChanged = prevRotation != currentRotation; 534 535 assertEquals("The activity should get the final display rotation in onCreate", 536 currentRotation, onCreateConfigInfo.rotation); 537 assertEquals("The application should get the final display rotation in onCreate", 538 currentRotation, appConfigInfo.rotation); 539 assertEquals("The orientation of application must be landscape", 540 ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation); 541 assertEquals("The orientation of system resources must be landscape", 542 ORIENTATION_LANDSCAPE, globalSizeInfo.orientation); 543 544 final boolean isLandscape = onCreateSize.displayWidth > onCreateSize.displayHeight; 545 if (displayRotationChanged) { 546 assertEquals("The activity should get the final display size in onCreate", 547 realDisplaySize, onCreateRealDisplaySize); 548 assertEquals("The app size of activity should have the same orientation", isLandscape, 549 realDisplaySize.x > realDisplaySize.y); 550 assertEquals("The display metrics of system resources must be landscape", isLandscape, 551 globalSizeInfo.metricsWidth > globalSizeInfo.metricsHeight); 552 } 553 assertEquals("The application should get the same orientation", isLandscape, 554 appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight); 555 assertEquals("The app display metrics must be landscape", isLandscape, 556 appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight); 557 } 558 559 @Test testTranslucentActivityPermitted()560 public void testTranslucentActivityPermitted() throws Exception { 561 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 562 563 disableIgnoreOrientationRequest(); 564 565 final RotationSession rotationSession = createManagedRotationSession(); 566 rotationSession.set(ROTATION_0); 567 568 launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 569 assumeNotIgnoringOrientation(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 570 mWmState.assertResumedActivity( 571 "target SDK <= 26 translucent activity should be allowed to launch", 572 SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY); 573 assertEquals("translucent activity requested landscape orientation", 574 SCREEN_ORIENTATION_LANDSCAPE, mWmState.getLastOrientation()); 575 } 576 577 /** 578 * Test that device handles moving between two tasks with different orientations. 579 */ 580 @Test testTaskCloseRestoreFixedOrientation()581 public void testTaskCloseRestoreFixedOrientation() { 582 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 583 disableIgnoreOrientationRequest(); 584 585 // Start landscape activity. 586 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 587 assumeFalse("Skipping test: device is fixed to user rotation", 588 mWmState.isFixedToUserRotation()); 589 assumeNotIgnoringOrientation(LANDSCAPE_ORIENTATION_ACTIVITY); 590 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 591 mWmState.waitAndAssertLastOrientation("Fullscreen app requested landscape orientation", 592 SCREEN_ORIENTATION_LANDSCAPE); 593 594 // Start another activity in a different task. 595 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 596 597 // Request portrait 598 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 599 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 600 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 601 602 // Finish activity 603 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 604 605 // Verify that activity brought to front is in originally requested orientation. 606 mWmState.computeState(LANDSCAPE_ORIENTATION_ACTIVITY); 607 mWmState.waitAndAssertLastOrientation("Should return to app in landscape orientation", 608 SCREEN_ORIENTATION_LANDSCAPE); 609 } 610 611 /** 612 * Test that device handles moving between two tasks with different orientations. 613 * 614 * TODO(b/139936670, b/112688380): This test case fails on some vendor devices which has 615 * rotation sensing optimization. So this is listed in cts-known-failures.xml. 616 */ 617 @Test testTaskCloseRestoreFreeOrientation()618 public void testTaskCloseRestoreFreeOrientation() { 619 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 620 621 // Start landscape activity. 622 launchActivity(RESIZEABLE_ACTIVITY); 623 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 624 final int initialServerOrientation = mWmState.getLastOrientation(); 625 626 // Verify fixed-landscape 627 separateTestJournal(); 628 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 629 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_LANDSCAPE); 630 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE); 631 waitForBroadcastActivityReady(ORIENTATION_LANDSCAPE); 632 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 633 634 // Verify that activity brought to front is in originally requested orientation. 635 mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED); 636 mWmState.waitAndAssertLastOrientation("Should come back in original server orientation", 637 initialServerOrientation); 638 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 639 0 /* numConfigChange */); 640 641 // Verify fixed-portrait 642 separateTestJournal(); 643 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 644 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 645 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 646 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 647 mBroadcastActionTrigger.finishBroadcastReceiverActivity(); 648 649 // Verify that activity brought to front is in originally requested orientation. 650 mWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED); 651 mWmState.waitAndAssertLastOrientation("Should come back in original server orientation", 652 initialServerOrientation); 653 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 654 0 /* numConfigChange */); 655 } 656 657 /** 658 * Test that activity orientation will change when device is rotated. 659 * Also verify that occluded activity will not get config changes. 660 */ 661 @Test testAppOrientationWhenRotating()662 public void testAppOrientationWhenRotating() throws Exception { 663 assumeFalse("Skipping test: square size may not have configuration changes", 664 isCloseToSquareDisplay()); 665 assumeTrue("Skipping test: no rotation support", supportsRotation()); 666 667 // Start resizeable activity that handles configuration changes. 668 separateTestJournal(); 669 launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 670 launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 671 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 672 673 final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY); 674 675 // Rotate the activity and check that it receives configuration changes with a different 676 // orientation each time. 677 final RotationSession rotationSession = createManagedRotationSession(); 678 assumeTrue("Skipping test: no locked user rotation mode support.", 679 supportsLockedUserRotation(rotationSession, displayId)); 680 681 rotationSession.set(ROTATION_0); 682 SizeInfo reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 683 int prevOrientation = reportedSizes.orientation; 684 685 final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 }; 686 for (final int rotation : rotations) { 687 separateTestJournal(); 688 rotationSession.set(rotation); 689 690 // Verify lifecycle count and orientation changes. 691 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 692 1 /* numConfigChange */); 693 reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 694 assertNotEquals(prevOrientation, reportedSizes.orientation); 695 assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */, 696 0 /* numConfigChange */); 697 698 prevOrientation = reportedSizes.orientation; 699 } 700 } 701 702 /** 703 * Test that the orientation for a simulated display context derived from an application context 704 * will not change when the device rotates. 705 */ 706 @Test testAppContextDerivedDisplayContextOrientationWhenRotating()707 public void testAppContextDerivedDisplayContextOrientationWhenRotating() { 708 assumeTrue("Skipping test: no rotation support", supportsRotation()); 709 assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay()); 710 711 assertDisplayContextDoesntChangeOrientationWhenRotating(Activity::getApplicationContext); 712 } 713 714 /** 715 * Test that the orientation for a simulated display context derived from an activity context 716 * will not change when the device rotates. 717 */ 718 @Test testActivityContextDerivedDisplayContextOrientationWhenRotating()719 public void testActivityContextDerivedDisplayContextOrientationWhenRotating() { 720 assumeTrue("Skipping test: no rotation support", supportsRotation()); 721 assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay()); 722 723 assertDisplayContextDoesntChangeOrientationWhenRotating(activity -> activity); 724 } 725 726 /** 727 * Asserts that the orientation for a simulated display context derived from a base context will 728 * not change when the device rotates. 729 * 730 * @param baseContextSupplier function that returns a base context used to created the display 731 * context. 732 * 733 * @see #testAppContextDerivedDisplayContextOrientationWhenRotating 734 * @see #testActivityContextDerivedDisplayContextOrientationWhenRotating 735 */ assertDisplayContextDoesntChangeOrientationWhenRotating( Function<Activity, Context> baseContextSupplier)736 private void assertDisplayContextDoesntChangeOrientationWhenRotating( 737 Function<Activity, Context> baseContextSupplier) { 738 RotationSession rotationSession = createManagedRotationSession(); 739 rotationSession.set(ROTATION_0); 740 741 TestActivitySession<ConfigChangeHandlingActivity> activitySession 742 = createManagedTestActivitySession(); 743 activitySession.launchTestActivityOnDisplaySync( 744 ConfigChangeHandlingActivity.class, 745 Display.DEFAULT_DISPLAY, 746 WINDOWING_MODE_FULLSCREEN); 747 final ConfigChangeHandlingActivity activity = activitySession.getActivity(); 748 749 VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession(); 750 WindowManagerState.DisplayContent displayContent = virtualDisplaySession 751 .setSimulateDisplay(true) 752 .setSimulationDisplaySize(100 /* width */, 200 /* height */) 753 .createDisplay(); 754 755 DisplayManager dm = activity.getSystemService(DisplayManager.class); 756 Display simulatedDisplay = dm.getDisplay(displayContent.mId); 757 Context simulatedDisplayContext = baseContextSupplier.apply(activity) 758 .createDisplayContext(simulatedDisplay); 759 assertEquals(ORIENTATION_PORTRAIT, 760 simulatedDisplayContext.getResources().getConfiguration().orientation); 761 762 separateTestJournal(); 763 764 final int[] rotations = {ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0}; 765 for (final int rotation : rotations) { 766 rotationSession.set(rotation); 767 768 assertRelaunchOrConfigChanged(activity.getComponentName(), 0 /* numRelaunch */, 769 1 /* numConfigChange */); 770 separateTestJournal(); 771 772 assertEquals("Display context orientation must not be changed", ORIENTATION_PORTRAIT, 773 simulatedDisplayContext.getResources().getConfiguration().orientation); 774 } 775 } 776 777 /** 778 * Test that activity orientation will not change when trying to rotate fixed-orientation 779 * activity. 780 * Also verify that occluded activity will not get config changes. 781 */ 782 @Test testFixedOrientationWhenRotating()783 public void testFixedOrientationWhenRotating() { 784 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 785 // TODO(b/110533226): Fix test on devices with display cutout 786 assumeFalse("Skipping test: display cutout present, can't predict exact lifecycle", 787 hasDisplayCutout()); 788 disableIgnoreOrientationRequest(); 789 790 // Start portrait-fixed activity 791 separateTestJournal(); 792 final ActivitySession activitySession = createManagedActivityClientSession() 793 .startActivity(getLaunchActivityBuilder() 794 .setUseInstrumentation() 795 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 796 .setTargetActivity(RESIZEABLE_ACTIVITY)); 797 mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); 798 799 final int displayId = mWmState.getDisplayByActivity(RESIZEABLE_ACTIVITY); 800 801 final RotationSession rotationSession = createManagedRotationSession(); 802 assumeTrue("Skipping test: no user locked rotation support.", 803 supportsLockedUserRotation(rotationSession, displayId)); 804 805 launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 806 assumeNotIgnoringOrientation(PORTRAIT_ORIENTATION_ACTIVITY); 807 mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */); 808 final SizeInfo initialSize = activitySession.getConfigInfo().sizeInfo; 809 810 // Rotate the display and check that the orientation doesn't change 811 rotationSession.set(ROTATION_0); 812 final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 }; 813 for (final int rotation : rotations) { 814 separateTestJournal(); 815 rotationSession.set(rotation, false /* waitDeviceRotation */); 816 817 // Verify lifecycle count and orientation changes. 818 assertRelaunchOrConfigChanged(PORTRAIT_ORIENTATION_ACTIVITY, 0 /* numRelaunch */, 819 0 /* numConfigChange */); 820 final SizeInfo currentSize = activitySession.getConfigInfo().sizeInfo; 821 assertEquals("Sizes must not be changed", initialSize, currentSize); 822 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */, 823 0 /* numConfigChange */); 824 } 825 } 826 827 /** 828 * Test that device handles moving between two tasks with different orientations. 829 */ 830 @Test testTaskMoveToBackOrientation()831 public void testTaskMoveToBackOrientation() { 832 assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest()); 833 disableIgnoreOrientationRequest(); 834 835 // Start landscape activity. 836 launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN); 837 assumeNotIgnoringOrientation(LANDSCAPE_ORIENTATION_ACTIVITY); 838 mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */); 839 final boolean isFixedToUserRotation = mWmState.isFixedToUserRotation(); 840 if (!isFixedToUserRotation) { 841 mWmState.waitAndAssertLastOrientation( 842 "Fullscreen app requested landscape orientation", 843 SCREEN_ORIENTATION_LANDSCAPE); 844 } else { 845 final SizeInfo reportedSizes = 846 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 847 assertEquals("landscape activity should be in landscape", 848 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 849 assertTrue("landscape activity should have height < width", 850 reportedSizes.heightDp < reportedSizes.widthDp); 851 } 852 853 // Start another activity in a different task. 854 launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY); 855 856 // Request portrait 857 mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT); 858 mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT); 859 waitForBroadcastActivityReady(ORIENTATION_PORTRAIT); 860 861 // Finish activity 862 mBroadcastActionTrigger.moveTopTaskToBack(); 863 864 // Verify that activity brought to front is in originally requested orientation. 865 mWmState.waitForValidState(LANDSCAPE_ORIENTATION_ACTIVITY); 866 if (!isFixedToUserRotation) { 867 mWmState.waitAndAssertLastOrientation( 868 "Fullscreen app requested landscape orientation", 869 SCREEN_ORIENTATION_LANDSCAPE); 870 } else { 871 final SizeInfo reportedSizes = 872 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY); 873 assertEquals("landscape activity should be in landscape", 874 ORIENTATION_LANDSCAPE, reportedSizes.orientation); 875 assertTrue("landscape activity should have height < width", 876 reportedSizes.heightDp < reportedSizes.widthDp); 877 } 878 } 879 880 /** 881 * Test that device doesn't change device orientation by app request while in multi-window. 882 */ 883 @Test testSplitscreenPortraitAppOrientationRequests()884 public void testSplitscreenPortraitAppOrientationRequests() throws Exception { 885 requestOrientationInSplitScreen(createManagedRotationSession(), 886 isDisplayPortrait() ? ROTATION_0 : ROTATION_90, LANDSCAPE_ORIENTATION_ACTIVITY); 887 } 888 889 /** 890 * Test that device doesn't change device orientation by app request while in multi-window. 891 */ 892 @Test testSplitscreenLandscapeAppOrientationRequests()893 public void testSplitscreenLandscapeAppOrientationRequests() throws Exception { 894 requestOrientationInSplitScreen(createManagedRotationSession(), 895 isDisplayPortrait() ? ROTATION_90 : ROTATION_0, PORTRAIT_ORIENTATION_ACTIVITY); 896 } 897 898 /** 899 * Launch specified activity in split-screen then rotate the divice, checking orientation 900 * should always change by the device rotation. 901 */ requestOrientationInSplitScreen(RotationSession rotationSession, int rotation, ComponentName activity)902 private void requestOrientationInSplitScreen(RotationSession rotationSession, int rotation, 903 ComponentName activity) throws Exception { 904 assumeTrue("Skipping test: no rotation support", supportsRotation()); 905 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 906 907 // Launch activity and move it to primary split-screen. 908 launchActivityInPrimarySplit(LAUNCHING_ACTIVITY); 909 910 // Launch target activity in secondary split-screen. 911 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); 912 getLaunchActivityBuilder() 913 .setTargetActivity(activity) 914 .setUseInstrumentation() 915 .setWaitForLaunched(true) 916 .setNewTask(true) 917 .setMultipleTask(true) 918 .execute(); 919 mWmState.assertVisibility(activity, true /* visible */); 920 921 // Rotate the device and it should always rotate regardless orientation app requested. 922 rotationSession.set(rotation); 923 assertEquals("Split-screen apps shouldn't influence device orientation", 924 rotation, mWmState.getRotation()); 925 926 // Launch target activity during split-screen again and check orientation still not change. 927 mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); 928 getLaunchActivityBuilder().setMultipleTask(true).setTargetActivity(activity).execute(); 929 mWmState.computeState(activity); 930 mWmState.assertVisibility(activity, true /* visible */); 931 assertEquals("Split-screen apps shouldn't influence device orientation", 932 rotation, mWmState.getRotation()); 933 } 934 935 /** 936 * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration 937 * have flipped. 938 */ assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB, boolean skipOrientationCheck)939 private static void assertSizesRotate(SizeInfo rotationA, SizeInfo rotationB, 940 boolean skipOrientationCheck) { 941 assertEquals(rotationA.displayWidth, rotationA.metricsWidth); 942 assertEquals(rotationA.displayHeight, rotationA.metricsHeight); 943 assertEquals(rotationB.displayWidth, rotationB.metricsWidth); 944 assertEquals(rotationB.displayHeight, rotationB.metricsHeight); 945 946 if (skipOrientationCheck) { 947 // All done if we are not doing orientation check. 948 return; 949 } 950 final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight; 951 final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight; 952 assertFalse(beforePortrait == afterPortrait); 953 954 final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp; 955 final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp; 956 assertEquals(beforePortrait, beforeConfigPortrait); 957 assertEquals(afterPortrait, afterConfigPortrait); 958 959 assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp); 960 } 961 962 /** 963 * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio) 964 * that are smaller than the dockedSizes. 965 */ 966 private static void assertSizesAreSane(SizeInfo fullscreenSizes, SizeInfo dockedSizes) { 967 final boolean isHorizontalDivision = 968 fullscreenSizes.displayHeight - dockedSizes.displayHeight > 969 fullscreenSizes.displayWidth - dockedSizes.displayWidth; 970 if (isHorizontalDivision) { 971 assertThat(dockedSizes.displayHeight, lessThan(fullscreenSizes.displayHeight)); 972 assertThat(dockedSizes.heightDp, lessThan(fullscreenSizes.heightDp)); 973 assertThat(dockedSizes.metricsHeight, lessThan(fullscreenSizes.metricsHeight)); 974 } else { 975 assertThat(dockedSizes.displayWidth, lessThan(fullscreenSizes.displayWidth)); 976 assertThat(dockedSizes.widthDp, lessThan(fullscreenSizes.widthDp)); 977 assertThat(dockedSizes.metricsWidth, lessThan(fullscreenSizes.metricsWidth)); 978 } 979 } 980 981 /** 982 * Throws an AssertionError if sizes are different. 983 */ 984 private static void assertSizesAreSame(SizeInfo firstSize, SizeInfo secondSize) { 985 assertEquals(firstSize.widthDp, secondSize.widthDp); 986 assertEquals(firstSize.heightDp, secondSize.heightDp); 987 assertEquals(firstSize.displayWidth, secondSize.displayWidth); 988 assertEquals(firstSize.displayHeight, secondSize.displayHeight); 989 assertEquals(firstSize.metricsWidth, secondSize.metricsWidth); 990 assertEquals(firstSize.metricsHeight, secondSize.metricsHeight); 991 assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp); 992 } 993 994 private void waitForBroadcastActivityReady(int orientation) { 995 mWmState.waitForActivityOrientation(BROADCAST_RECEIVER_ACTIVITY, orientation); 996 mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED); 997 } 998 999 /** 1000 * Test launching an activity which requests specific UI mode during creation. 1001 */ 1002 @Test 1003 public void testLaunchWithUiModeChange() { 1004 // Launch activity that changes UI mode and handles this configuration change. 1005 launchActivity(NIGHT_MODE_ACTIVITY); 1006 mWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED); 1007 1008 // Check if activity is launched successfully. 1009 mWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */); 1010 mWmState.assertFocusedActivity("Launched activity should be focused", 1011 NIGHT_MODE_ACTIVITY); 1012 mWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_ACTIVITY); 1013 } 1014 1015 @Test 1016 public void testAppConfigurationMatchesActivityInMultiWindow() throws Exception { 1017 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 1018 1019 final ActivitySession activitySession = createManagedActivityClientSession() 1020 .startActivity(getLaunchActivityBuilder() 1021 .setUseInstrumentation() 1022 .setTargetActivity(RESIZEABLE_ACTIVITY) 1023 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 1024 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1025 SizeInfo dockedActivitySizes = getActivitySizeInfo(activitySession); 1026 SizeInfo applicationSizes = getAppSizeInfo(activitySession); 1027 assertSizesAreSame(dockedActivitySizes, applicationSizes); 1028 1029 // Move the activity to fullscreen and check that the size was updated 1030 separateTestJournal(); 1031 mTaskOrganizer.dismissSplitScreen(true /* primaryOnTop */); 1032 waitForOrFail("Activity and application configuration must match", 1033 () -> activityAndAppSizesMatch(activitySession)); 1034 final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY); 1035 applicationSizes = getAppSizeInfo(activitySession); 1036 assertSizesAreSane(fullscreenSizes, dockedActivitySizes); 1037 assertSizesAreSame(fullscreenSizes, applicationSizes); 1038 1039 // Move the activity to docked size again, check if the sizes were updated 1040 separateTestJournal(); 1041 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1042 waitForOrFail("Activity and application configuration must match", 1043 () -> activityAndAppSizesMatch(activitySession)); 1044 dockedActivitySizes = getActivitySizeInfo(activitySession); 1045 applicationSizes = getAppSizeInfo(activitySession); 1046 assertSizesAreSane(fullscreenSizes, dockedActivitySizes); 1047 assertSizesAreSame(dockedActivitySizes, applicationSizes); 1048 } 1049 1050 @Test 1051 public void testAppConfigurationMatchesTopActivityInMultiWindow() throws Exception { 1052 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 1053 1054 // Launch initial activity in fullscreen and assert sizes 1055 final ActivitySession fullscreenActivitySession = createManagedActivityClientSession() 1056 .startActivity(getLaunchActivityBuilder() 1057 .setUseInstrumentation() 1058 .setTargetActivity(TEST_ACTIVITY) 1059 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)); 1060 SizeInfo fullscreenActivitySizes = getActivitySizeInfo(fullscreenActivitySession); 1061 SizeInfo applicationSizes = getAppSizeInfo(fullscreenActivitySession); 1062 assertSizesAreSame(fullscreenActivitySizes, applicationSizes); 1063 1064 // Launch second activity in split-screen and assert that sizes were updated 1065 separateTestJournal(); 1066 final ActivitySession secondActivitySession = createManagedActivityClientSession() 1067 .startActivity(getLaunchActivityBuilder() 1068 .setUseInstrumentation() 1069 .setTargetActivity(RESIZEABLE_ACTIVITY) 1070 .setNewTask(true) 1071 .setMultipleTask(true)); 1072 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1073 waitForOrFail("Activity and application configuration must match", 1074 () -> activityAndAppSizesMatch(secondActivitySession)); 1075 SizeInfo dockedActivitySizes = getActivitySizeInfo(secondActivitySession); 1076 applicationSizes = getAppSizeInfo(secondActivitySession); 1077 assertSizesAreSame(dockedActivitySizes, applicationSizes); 1078 assertSizesAreSane(fullscreenActivitySizes, dockedActivitySizes); 1079 1080 // Launch third activity in secondary split-screen and assert that sizes were updated 1081 separateTestJournal(); 1082 final ActivitySession thirdActivitySession = createManagedActivityClientSession() 1083 .startActivity(getLaunchActivityBuilder() 1084 .setUseInstrumentation() 1085 .setTargetActivity(RESIZEABLE_ACTIVITY) 1086 .setNewTask(true) 1087 .setMultipleTask(true)); 1088 putActivityInPrimarySplit(RESIZEABLE_ACTIVITY); 1089 waitForOrFail("Activity and application configuration must match", 1090 () -> activityAndAppSizesMatch(thirdActivitySession)); 1091 SizeInfo secondarySplitActivitySizes = getActivitySizeInfo(thirdActivitySession); 1092 applicationSizes = getAppSizeInfo(thirdActivitySession); 1093 assertSizesAreSame(secondarySplitActivitySizes, applicationSizes); 1094 assertSizesAreSane(fullscreenActivitySizes, secondarySplitActivitySizes); 1095 } 1096 1097 @Test testAppConfigurationMatchesActivityInFreeform()1098 public void testAppConfigurationMatchesActivityInFreeform() throws Exception { 1099 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 1100 1101 // Launch activity in freeform and assert sizes 1102 final ActivitySession freeformActivitySession = createManagedActivityClientSession() 1103 .startActivity(getLaunchActivityBuilder() 1104 .setUseInstrumentation() 1105 .setTargetActivity(TEST_ACTIVITY) 1106 .setWindowingMode(WINDOWING_MODE_FREEFORM)); 1107 SizeInfo freeformActivitySizes = getActivitySizeInfo(freeformActivitySession); 1108 SizeInfo applicationSizes = getAppSizeInfo(freeformActivitySession); 1109 assertSizesAreSame(freeformActivitySizes, applicationSizes); 1110 } 1111 activityAndAppSizesMatch(ActivitySession activitySession)1112 private boolean activityAndAppSizesMatch(ActivitySession activitySession) { 1113 final SizeInfo activitySize = activitySession.getConfigInfo().sizeInfo; 1114 final SizeInfo appSize = activitySession.getAppConfigInfo().sizeInfo; 1115 return activitySize.equals(appSize); 1116 } 1117 getActivitySizeInfo(ActivitySession activitySession)1118 private SizeInfo getActivitySizeInfo(ActivitySession activitySession) { 1119 return activitySession.getConfigInfo().sizeInfo; 1120 } 1121 getAppSizeInfo(ActivitySession activitySession)1122 private SizeInfo getAppSizeInfo(ActivitySession activitySession) { 1123 return activitySession.getAppConfigInfo().sizeInfo; 1124 } 1125 assumeNotIgnoringOrientation(ComponentName activityName)1126 private void assumeNotIgnoringOrientation(ComponentName activityName) { 1127 assumeFalse("Skipping test: display area is ignoring orientation request", 1128 getWmState().isTaskDisplayAreaIgnoringOrientationRequest(activityName)); 1129 } 1130 } 1131