1 /* 2 * Copyright (C) 2023 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 android.server.wm.activity; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 21 import static junit.framework.Assert.assertFalse; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertNotEquals; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assume.assumeFalse; 27 import static org.junit.Assume.assumeTrue; 28 29 import android.content.ComponentCallbacks; 30 import android.content.res.Configuration; 31 import android.graphics.Rect; 32 import android.hardware.display.DisplayManager; 33 import android.hardware.display.DisplayManager.DisplayListener; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.os.RemoteException; 37 import android.os.SystemClock; 38 import android.platform.test.annotations.Presubmit; 39 import android.platform.test.annotations.RequiresFlagsEnabled; 40 import android.platform.test.flag.junit.CheckFlagsRule; 41 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 42 import android.server.wm.RotationSession; 43 import android.server.wm.WindowManagerTestBase; 44 import android.util.Log; 45 import android.util.Size; 46 import android.view.Display; 47 48 import androidx.annotation.NonNull; 49 50 import com.android.compatibility.common.util.ApiTest; 51 import com.android.window.flags.Flags; 52 53 import org.junit.After; 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 58 /** 59 * Tests that verify the behavior of client side window configuration related state changed 60 * callbacks, such as {@link DisplayListener}, to ensure that they are synchronized with the client 61 * side {@link Configuration} change. 62 * 63 * Build/Install/Run: 64 * atest CtsWindowManagerDeviceActivity:ConfigurationCallbacksTest 65 */ 66 @Presubmit 67 public class ConfigurationCallbacksTest extends WindowManagerTestBase { 68 69 @Rule 70 public final CheckFlagsRule mCheckFlagsRule = 71 DeviceFlagsValueProvider.createCheckFlagsRule(); 72 73 private static final String TAG = ConfigurationCallbacksTest.class.getSimpleName(); 74 75 private ReportedDisplayMetrics mReportedDisplayMetrics; 76 77 private WindowConfigTracker mDisplayListenerTracker; 78 private WindowConfigTracker mActivityOnConfigurationChangedTracker; 79 private WindowConfigTracker mApplicationOnConfigurationChangedTracker; 80 81 private TestComponentCallbacks mApplicationCallbacks; 82 private TestDisplayListener mDisplayListener; 83 private TestActivity mActivity; 84 85 @Before setUp()86 public void setUp() throws Exception { 87 super.setUp(); 88 89 mReportedDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(Display.DEFAULT_DISPLAY); 90 91 mDisplayListenerTracker = new WindowConfigTracker("DisplayListener"); 92 mActivityOnConfigurationChangedTracker = new WindowConfigTracker( 93 "Activity#onConfigurationChanged"); 94 // Application callback is expected to be triggered before Activity Config update. 95 mApplicationOnConfigurationChangedTracker = new WindowConfigTracker( 96 "Application#onConfigurationChanged", true /* excludeActivity */); 97 98 mActivity = startActivityInWindowingModeFullScreen(TestActivity.class); 99 waitAndAssertResumedActivity(mActivity.getComponentName(), "The activity must be resumed."); 100 101 mActivity.setWindowConfigTracker(mActivityOnConfigurationChangedTracker); 102 103 mApplicationCallbacks = new TestComponentCallbacks( 104 mApplicationOnConfigurationChangedTracker); 105 mActivity.getApplication().registerComponentCallbacks(mApplicationCallbacks); 106 107 mDisplayListener = new TestDisplayListener(mDisplayListenerTracker); 108 mDm.registerDisplayListener(mDisplayListener, new Handler(Looper.getMainLooper())); 109 } 110 111 @After tearDown()112 public void tearDown() throws RemoteException { 113 if (mDisplayListener != null) { 114 mDm.unregisterDisplayListener(mDisplayListener); 115 } 116 if (mActivity != null) { 117 mActivity.getApplication().unregisterComponentCallbacks(mApplicationCallbacks); 118 mActivity.finish(); 119 } 120 if (mReportedDisplayMetrics != null) { 121 mReportedDisplayMetrics.restoreDisplayMetrics(); 122 } 123 } 124 125 /** 126 * Verifies that when the display rotates, the last triggered 127 * {@link DisplayListener#onDisplayChanged} have updated {@link android.app.WindowConfiguration} 128 * that is synchronized with the display window. 129 */ 130 @RequiresFlagsEnabled(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG) 131 @Test 132 @ApiTest(apis = { 133 "android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged", 134 "android.app.Activity#onConfigurationChanged", 135 "android.content.ComponentCallbacks#onConfigurationChanged", 136 }) testDisplayRotate()137 public void testDisplayRotate() { 138 assumeTrue(supportsRotation()); 139 // Devices that always launch activities in multi-window may not be able to update 140 // Task bounds in the same transaction with display bounds (maxBounds) changed. 141 assumeFalse(mActivity.isInMultiWindowMode()); 142 143 final RotationSession rotationSession = createManagedRotationSession(); 144 int rotation = rotationSession.get(); 145 for (int i = 0; i < 4; i++) { 146 rotation = (rotation + 1) % 4; 147 initTrackers(); 148 rotationSession.set(rotation); 149 waitAndAssertRotationInCallbacks(rotation); 150 } 151 } 152 153 /** 154 * Verifies that when the display resizes, the last triggered 155 * {@link DisplayListener#onDisplayChanged} have updated {@link android.app.WindowConfiguration} 156 * that is synchronized with the display window. 157 */ 158 @RequiresFlagsEnabled(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG) 159 @Test 160 @ApiTest(apis = { 161 "android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged", 162 "android.app.Activity#onConfigurationChanged", 163 "android.content.ComponentCallbacks#onConfigurationChanged", 164 }) testDisplayResize()165 public void testDisplayResize() { 166 // Devices that always launch activities in multi-window may not be able to update 167 // Task bounds in the same transaction with display bounds (maxBounds) changed. 168 assumeFalse(mActivity.isInMultiWindowMode()); 169 170 final Size originalSize = mReportedDisplayMetrics.getSize(); 171 // Use a negative offset in case the device set config_maxUiWidth. 172 final int offset = -Math.min(originalSize.getWidth() / 10, originalSize.getHeight() / 10); 173 final int newWidth = originalSize.getWidth() + offset; 174 final int newHeight = originalSize.getHeight() + offset; 175 assumeTrue("Can't resize the display smaller than min size", 176 newWidth >= 200 && newHeight >= 200); 177 178 initTrackers(); 179 mReportedDisplayMetrics.setSize(new Size(newWidth, newHeight)); 180 if (hasAutomotiveSplitscreenMultitaskingFeature()) { 181 // On Automotive SplitScreen Multitasking devices, the bounds of TDAs (& thus 182 // activities) may not grow linearly with the change in display bounds. 183 waitAndAssertBoundsChangeInCallbacks(); 184 } else { 185 waitAndAssertDimensionsOffsetInCallbacks(offset); 186 } 187 } 188 189 /** 190 * Similar to {@link #testDisplayResize()}, but works for devices that always launch activities 191 * in multi-window to make sure the display bounds is always up-to-date. 192 */ 193 @RequiresFlagsEnabled(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG) 194 @Test 195 @ApiTest(apis = { 196 "android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged", 197 "android.app.Activity#onConfigurationChanged", 198 "android.content.ComponentCallbacks#onConfigurationChanged", 199 }) testDisplayResizeForDisplayBoundsOnly()200 public void testDisplayResizeForDisplayBoundsOnly() { 201 final Size originalSize = mReportedDisplayMetrics.getSize(); 202 // Use a negative offset in case the device set config_maxUiWidth. 203 final int offset = -Math.min(originalSize.getWidth() / 10, originalSize.getHeight() / 10); 204 final int newWidth = originalSize.getWidth() + offset; 205 final int newHeight = originalSize.getHeight() + offset; 206 assumeTrue("Can't resize the display smaller than min size", 207 newWidth >= 200 && newHeight >= 200); 208 209 initTrackers(); 210 mReportedDisplayMetrics.setSize(new Size(newWidth, newHeight)); 211 212 if (hasAutomotiveSplitscreenMultitaskingFeature()) { 213 // On Automotive SplitScreen Multitasking devices, the bounds of TDAs (& thus 214 // activities) may not grow linearly with the change in display bounds. 215 waitAndAssertDisplayBoundsChangeInCallbacks(); 216 } else { 217 waitAndAssertDisplayOffsetInCallbacks(offset); 218 } 219 } 220 221 /** 222 * Initializes {@link WindowConfigTracker}s. 223 * Should be called before triggering the test system event. 224 */ initTrackers()225 private void initTrackers() { 226 Log.d(TAG, "initTrackers"); 227 mDisplayListenerTracker.init(); 228 mActivityOnConfigurationChangedTracker.init(); 229 mApplicationOnConfigurationChangedTracker.init(); 230 } 231 232 /** 233 * Waits and asserts that the last system callbacks must come with the given display rotation. 234 */ waitAndAssertRotationInCallbacks(int expectedRotation)235 private void waitAndAssertRotationInCallbacks(int expectedRotation) { 236 final long curTime = SystemClock.elapsedRealtime(); 237 mDisplayListenerTracker.waitAndAssertRotation(expectedRotation, curTime); 238 mActivityOnConfigurationChangedTracker.waitAndAssertRotation(expectedRotation, curTime); 239 mApplicationOnConfigurationChangedTracker.waitAndAssertRotation(expectedRotation, curTime); 240 } 241 242 /** 243 * Waits and asserts that the last system callbacks must come with display dimensions which are 244 * different from the initial dimensions. 245 */ waitAndAssertBoundsChangeInCallbacks()246 private void waitAndAssertBoundsChangeInCallbacks() { 247 final long curTime = SystemClock.elapsedRealtime(); 248 mDisplayListenerTracker.waitAndAssertBoundsChange(curTime); 249 mActivityOnConfigurationChangedTracker.waitAndAssertBoundsChange(curTime); 250 mApplicationOnConfigurationChangedTracker.waitAndAssertBoundsChange(curTime); 251 } 252 253 /** 254 * Waits and asserts that the last system callbacks must come with display dimensions with the 255 * given offset from the current dimensions. 256 * 257 * Note: the same offset will be used for both width and height because the display size set 258 * through {@link ReportedDisplayMetrics} is independent to the display rotation. 259 */ waitAndAssertDimensionsOffsetInCallbacks(int expectedOffset)260 private void waitAndAssertDimensionsOffsetInCallbacks(int expectedOffset) { 261 final long curTime = SystemClock.elapsedRealtime(); 262 mDisplayListenerTracker.waitAndAssertDimensionsOffset(expectedOffset, curTime); 263 mActivityOnConfigurationChangedTracker.waitAndAssertDimensionsOffset(expectedOffset, 264 curTime); 265 mApplicationOnConfigurationChangedTracker.waitAndAssertDimensionsOffset(expectedOffset, 266 curTime); 267 } 268 269 /** 270 * Similar to {@link #waitAndAssertBoundsChangeInCallbacks()}, but only verify display bounds. 271 */ waitAndAssertDisplayBoundsChangeInCallbacks()272 private void waitAndAssertDisplayBoundsChangeInCallbacks() { 273 final long curTime = SystemClock.elapsedRealtime(); 274 mDisplayListenerTracker.waitAndAssertDisplayBoundsChange(curTime); 275 mActivityOnConfigurationChangedTracker.waitAndAssertDisplayBoundsChange(curTime); 276 mApplicationOnConfigurationChangedTracker.waitAndAssertDisplayBoundsChange(curTime); 277 } 278 279 /** 280 * Similar to {@link #waitAndAssertDimensionsOffsetInCallbacks}, but only verify display bounds. 281 */ waitAndAssertDisplayOffsetInCallbacks(int expectedOffset)282 private void waitAndAssertDisplayOffsetInCallbacks(int expectedOffset) { 283 final long curTime = SystemClock.elapsedRealtime(); 284 mDisplayListenerTracker.waitAndAssertDisplayOffset(expectedOffset, curTime); 285 mActivityOnConfigurationChangedTracker.waitAndAssertDisplayOffset(expectedOffset, 286 curTime); 287 mApplicationOnConfigurationChangedTracker.waitAndAssertDisplayOffset(expectedOffset, 288 curTime); 289 } 290 291 /** 292 * Helper class to keep track and assert the window configuration in system callbacks. 293 * 294 * Use flow: 295 * 1. Calls {@link #init()} to reset tracked value, and record the initial config. 296 * 2. Applies any action to trigger system callbacks. 297 * 3. Calls {@link #waitAndAssertRotation} or {@link #waitAndAssertDimensionsOffset} 298 * to test that the last system callback has the current config. 299 */ 300 private class WindowConfigTracker { 301 302 private static final int INVALID_ROTATION = -1; 303 304 private static final long CALLBACK_TIMEOUT_MS = 2000L; 305 private static final long CALLBACK_TIMEOUT_MAX_RETRY = 4; 306 private static final long CALLBACK_TIMEOUT_MAX_WAITING_TIME_MS = 8000L; // TIMEOUT * RETRY 307 308 @NonNull 309 private final String mCallbackName; 310 private final boolean mExcludeActivity; 311 312 /** How many times the callback has been triggered since the last {@link #init()} */ 313 private int mCallbackCount; 314 315 /** The system time when the last system callback was triggered. */ 316 private long mLastCallbackSystemTime; 317 318 private int mInitDisplayRotation = INVALID_ROTATION; 319 private int mInitActivityRotation = INVALID_ROTATION; 320 private int mInitApplicationRotation = INVALID_ROTATION; 321 private final Rect mInitWindowMetricsBounds = new Rect(); 322 private final Rect mInitActivityBounds = new Rect(); 323 private final Rect mInitApplicationBounds = new Rect(); 324 325 private int mLastDisplayRotation = INVALID_ROTATION; 326 private int mLastActivityRotation = INVALID_ROTATION; 327 private int mLastApplicationRotation = INVALID_ROTATION; 328 private final Rect mLastWindowMetricsBounds = new Rect(); 329 private final Rect mLastActivityBounds = new Rect(); 330 private final Rect mLastApplicationBounds = new Rect(); 331 WindowConfigTracker(@onNull String callbackName)332 WindowConfigTracker(@NonNull String callbackName) { 333 this(callbackName, false /* excludeActivity */); 334 } 335 WindowConfigTracker(@onNull String callbackName, boolean excludeActivity)336 WindowConfigTracker(@NonNull String callbackName, boolean excludeActivity) { 337 mCallbackName = callbackName; 338 mExcludeActivity = excludeActivity; 339 } 340 341 /** Should be called before triggering the test system event. */ init()342 void init() { 343 // Reset counter and timer 344 mCallbackCount = 0; 345 mLastCallbackSystemTime = 0; 346 347 // Record the current config 348 mInitDisplayRotation = getDisplayRotation(); 349 mInitActivityRotation = getActivityRotation(); 350 mInitApplicationRotation = getApplicationRotation(); 351 mInitWindowMetricsBounds.set(getWindowMetricsBounds()); 352 mInitActivityBounds.set(getActivityBounds()); 353 mInitApplicationBounds.set(getApplicationBounds()); 354 355 // Reset the config from last system callback. 356 mLastDisplayRotation = INVALID_ROTATION; 357 mLastActivityRotation = INVALID_ROTATION; 358 mLastApplicationRotation = INVALID_ROTATION; 359 mLastWindowMetricsBounds.setEmpty(); 360 mLastActivityBounds.setEmpty(); 361 mLastApplicationBounds.setEmpty(); 362 } 363 364 /** 365 * Called when there is a system callback regarding the window config changed. 366 */ onWindowConfigChanged()367 void onWindowConfigChanged() { 368 mCallbackCount++; 369 mLastCallbackSystemTime = SystemClock.elapsedRealtime(); 370 371 mLastDisplayRotation = getDisplayRotation(); 372 mLastActivityRotation = getActivityRotation(); 373 mLastApplicationRotation = getApplicationRotation(); 374 mLastWindowMetricsBounds.set(getWindowMetricsBounds()); 375 mLastActivityBounds.set(getActivityBounds()); 376 mLastApplicationBounds.set(getApplicationBounds()); 377 } 378 379 /** 380 * Waits and asserts that the last system callback must come with the given display 381 * rotation. 382 */ waitAndAssertRotation(int expectedRotation, long startTime)383 void waitAndAssertRotation(int expectedRotation, long startTime) { 384 waitForLastCallbackTimeout(startTime); 385 assertCallbackTriggered(); 386 387 final String errorMessage = mCallbackName 388 + ": expect the last rotation to be " 389 + expectedRotation + ", but have:" 390 + "\ninitDisplayRotation=" + mInitDisplayRotation 391 + "\ninitActivityRotation=" + mInitActivityRotation 392 + "\ninitApplicationRotation=" + mInitApplicationRotation 393 + "\nlastDisplayRotation=" + mLastDisplayRotation 394 + "\nlastActivityRotation=" + mLastActivityRotation 395 + "\nlastApplicationRotation=" + mLastApplicationRotation 396 + "\nThe callback has been triggered for " + mCallbackCount + " times."; 397 assertTrue(errorMessage, mLastDisplayRotation == expectedRotation 398 && (mExcludeActivity || mLastActivityRotation == expectedRotation) 399 && mLastApplicationRotation == expectedRotation); 400 } 401 402 /** 403 * Waits and asserts that the last system callback must come with 404 * display/application/activity dimensions which is different from the initial dimensions. 405 */ waitAndAssertBoundsChange(long startTime)406 void waitAndAssertBoundsChange(long startTime) { 407 waitForLastCallbackTimeout(startTime); 408 assertCallbackTriggered(); 409 410 final String errorMessage = mCallbackName 411 + ": expect the bounds to change, but have:" 412 + "\ninitDisplayBounds=" + mInitWindowMetricsBounds 413 + "\ninitActivityBounds=" + mInitActivityBounds 414 + "\ninitApplicationBounds=" + mInitApplicationBounds 415 + "\nlastDisplayBounds=" + mLastWindowMetricsBounds 416 + "\nlastActivityBounds=" + mLastActivityBounds 417 + "\nlastApplicationBounds=" + mLastApplicationBounds 418 + "\nThe callback has been triggered for " + mCallbackCount + " times."; 419 assertFalse(errorMessage, mInitWindowMetricsBounds.equals(mLastWindowMetricsBounds) 420 || (!mExcludeActivity && mInitActivityBounds.equals(mLastActivityBounds)) 421 || mInitApplicationBounds.equals(mLastApplicationBounds)); 422 } 423 424 /** 425 * Waits and asserts that the last system callback must come with 426 * display/application/activity dimensions with the given offset from the initial 427 * dimensions. 428 * 429 * Note: the same offset will be used for both width and height because the display size set 430 * through {@link ReportedDisplayMetrics} is independent to the display rotation. 431 */ waitAndAssertDimensionsOffset(int expectedOffset, long startTime)432 void waitAndAssertDimensionsOffset(int expectedOffset, long startTime) { 433 waitForLastCallbackTimeout(startTime); 434 assertCallbackTriggered(); 435 436 final String errorMessage = mCallbackName 437 + ": expect the offset from last bounds right/bottom to be " 438 + expectedOffset + ", but have:" 439 + "\ninitDisplayBounds=" + mInitWindowMetricsBounds 440 + "\ninitActivityBounds=" + mInitActivityBounds 441 + "\ninitApplicationBounds=" + mInitApplicationBounds 442 + "\nlastDisplayBounds=" + mLastWindowMetricsBounds 443 + "\nlastActivityBounds=" + mLastActivityBounds 444 + "\nlastApplicationBounds=" + mLastApplicationBounds 445 + "\nThe callback has been triggered for " + mCallbackCount + " times."; 446 mInitWindowMetricsBounds.right += expectedOffset; 447 mInitWindowMetricsBounds.bottom += expectedOffset; 448 mInitActivityBounds.right += expectedOffset; 449 mInitActivityBounds.bottom += expectedOffset; 450 mInitApplicationBounds.right += expectedOffset; 451 mInitApplicationBounds.bottom += expectedOffset; 452 assertTrue(errorMessage, mInitWindowMetricsBounds.equals(mLastWindowMetricsBounds) 453 && (mExcludeActivity || mInitActivityBounds.equals(mLastActivityBounds)) 454 && mInitApplicationBounds.equals(mLastApplicationBounds)); 455 } 456 457 /** 458 * Similar to {@link #waitAndAssertBoundsChange(long)}, but only verify display 459 * bounds. 460 */ waitAndAssertDisplayBoundsChange(long startTime)461 void waitAndAssertDisplayBoundsChange(long startTime) { 462 waitForLastCallbackTimeout(startTime); 463 assertCallbackTriggered(); 464 465 final String errorMessage = mCallbackName 466 + ": expect the last display bounds to be different from initial bounds, " 467 + "but have:" 468 + "\ninitDisplayBounds=" + mInitWindowMetricsBounds 469 + "\nlastDisplayBounds=" + mLastWindowMetricsBounds 470 + "\nThe callback has been triggered for " + mCallbackCount + " times."; 471 assertNotEquals(errorMessage, mInitWindowMetricsBounds, mLastWindowMetricsBounds); 472 } 473 474 475 /** 476 * Similar to {@link #waitAndAssertDimensionsOffset}, but only verify display bounds. 477 */ waitAndAssertDisplayOffset(int expectedOffset, long startTime)478 void waitAndAssertDisplayOffset(int expectedOffset, long startTime) { 479 waitForLastCallbackTimeout(startTime); 480 assertCallbackTriggered(); 481 482 final String errorMessage = mCallbackName 483 + ": expect the offset from last display bounds right/bottom to be " 484 + expectedOffset + ", but have:" 485 + "\ninitDisplayBounds=" + mInitWindowMetricsBounds 486 + "\nlastDisplayBounds=" + mLastWindowMetricsBounds 487 + "\nThe callback has been triggered for " + mCallbackCount + " times."; 488 mInitWindowMetricsBounds.right += expectedOffset; 489 mInitWindowMetricsBounds.bottom += expectedOffset; 490 assertEquals(errorMessage, mInitWindowMetricsBounds, mLastWindowMetricsBounds); 491 } 492 493 /** 494 * Waits until there is enough time from the last callback. This is to ensure that there is 495 * no unexpected following callbacks with different config. 496 */ waitForLastCallbackTimeout(long startTime)497 private void waitForLastCallbackTimeout(long startTime) { 498 long curTime = SystemClock.elapsedRealtime(); 499 if (curTime - CALLBACK_TIMEOUT_MAX_WAITING_TIME_MS >= startTime) { 500 // No need to wait in case we have waited long enough in other Trackers. 501 return; 502 } 503 504 for (int i = 0; i < CALLBACK_TIMEOUT_MAX_RETRY; i++) { 505 curTime = SystemClock.elapsedRealtime(); 506 if (mCallbackCount > 0 507 && curTime - CALLBACK_TIMEOUT_MS >= mLastCallbackSystemTime) { 508 return; 509 } 510 Log.i(TAG, "*** Waiting for last callback " + mCallbackName + " IDLE retry=" + i); 511 SystemClock.sleep(CALLBACK_TIMEOUT_MS); 512 } 513 } 514 assertCallbackTriggered()515 private void assertCallbackTriggered() { 516 assertNotEquals(mCallbackName + ": callback has never been triggered", 517 0, mCallbackCount); 518 assertTrue(mCallbackName + ": the last callback didn't wait enough time before timeout", 519 SystemClock.elapsedRealtime() - CALLBACK_TIMEOUT_MS >= mLastCallbackSystemTime); 520 } 521 getDisplayRotation()522 private int getDisplayRotation() { 523 return mDm.getDisplay(DEFAULT_DISPLAY).getRotation(); 524 } 525 getActivityRotation()526 private int getActivityRotation() { 527 return mActivity.getResources() 528 .getConfiguration().windowConfiguration.getDisplayRotation(); 529 } 530 getApplicationRotation()531 private int getApplicationRotation() { 532 return mActivity.getApplicationContext().getResources() 533 .getConfiguration().windowConfiguration.getDisplayRotation(); 534 } 535 536 @NonNull getWindowMetricsBounds()537 private Rect getWindowMetricsBounds() { 538 return mWm.getMaximumWindowMetrics().getBounds(); 539 } 540 541 @NonNull getActivityBounds()542 private Rect getActivityBounds() { 543 return mActivity.getResources() 544 .getConfiguration().windowConfiguration.getBounds(); 545 } 546 547 @NonNull getApplicationBounds()548 private Rect getApplicationBounds() { 549 return mActivity.getApplicationContext().getResources() 550 .getConfiguration().windowConfiguration.getBounds(); 551 } 552 } 553 554 private static class TestComponentCallbacks implements ComponentCallbacks { 555 556 @NonNull 557 private final WindowConfigTracker mTracker; 558 TestComponentCallbacks(@onNull WindowConfigTracker tracker)559 TestComponentCallbacks(@NonNull WindowConfigTracker tracker) { 560 mTracker = tracker; 561 } 562 563 @Override onConfigurationChanged(@onNull Configuration newConfig)564 public void onConfigurationChanged(@NonNull Configuration newConfig) { 565 Log.d(TAG, "Application#onConfigurationChanged"); 566 mTracker.onWindowConfigChanged(); 567 } 568 569 @Override onLowMemory()570 public void onLowMemory() {} 571 } 572 573 private static class TestDisplayListener implements DisplayManager.DisplayListener { 574 575 @NonNull 576 private final WindowConfigTracker mTracker; 577 TestDisplayListener(@onNull WindowConfigTracker tracker)578 TestDisplayListener(@NonNull WindowConfigTracker tracker) { 579 mTracker = tracker; 580 } 581 582 @Override onDisplayAdded(int displayId)583 public void onDisplayAdded(int displayId) {} 584 585 @Override onDisplayRemoved(int displayId)586 public void onDisplayRemoved(int displayId) {} 587 588 @Override onDisplayChanged(int displayId)589 public void onDisplayChanged(int displayId) { 590 if (displayId == DEFAULT_DISPLAY) { 591 // Only test against the default display. 592 Log.d(TAG, "DisplayListener#onDisplayChanged"); 593 mTracker.onWindowConfigChanged(); 594 } 595 } 596 } 597 598 /** Activity to be used for verifying window state {@link #onConfigurationChanged}. */ 599 public static class TestActivity extends FocusableActivity { 600 601 private WindowConfigTracker mTracker; 602 603 /** Initializes to track the window state. */ setWindowConfigTracker(@onNull WindowConfigTracker tracker)604 void setWindowConfigTracker(@NonNull WindowConfigTracker tracker) { 605 mTracker = tracker; 606 } 607 608 @Override onConfigurationChanged(@onNull Configuration newConfig)609 public void onConfigurationChanged(@NonNull Configuration newConfig) { 610 super.onConfigurationChanged(newConfig); 611 if (mTracker != null) { 612 Log.d(TAG, "Activity#onConfigurationChanged"); 613 mTracker.onWindowConfigChanged(); 614 } 615 } 616 } 617 } 618