1 /* 2 * Copyright (C) 2012 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.display.cts; 18 19 import static android.content.pm.PackageManager.FEATURE_LEANBACK; 20 import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API; 21 import static android.view.Display.DEFAULT_DISPLAY; 22 import static android.view.Display.FRAME_RATE_CATEGORY_HIGH; 23 import static android.view.Display.FRAME_RATE_CATEGORY_NORMAL; 24 25 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 26 27 import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE; 28 import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES; 29 import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT; 30 31 import static com.google.common.truth.Truth.assertThat; 32 33 import static org.junit.Assert.assertArrayEquals; 34 import static org.junit.Assert.assertEquals; 35 import static org.junit.Assert.assertFalse; 36 import static org.junit.Assert.assertNotEquals; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNull; 39 import static org.junit.Assert.assertTrue; 40 import static org.junit.Assume.assumeNotNull; 41 import static org.junit.Assume.assumeTrue; 42 43 import android.Manifest; 44 import android.app.Activity; 45 import android.app.Presentation; 46 import android.app.UiAutomation; 47 import android.app.UiModeManager; 48 import android.content.Context; 49 import android.content.pm.PackageManager; 50 import android.content.res.Configuration; 51 import android.graphics.Color; 52 import android.graphics.ColorSpace; 53 import android.graphics.PixelFormat; 54 import android.graphics.Point; 55 import android.hardware.DataSpace; 56 import android.hardware.HardwareBuffer; 57 import android.hardware.LutProperties; 58 import android.hardware.OverlayProperties; 59 import android.hardware.display.DeviceProductInfo; 60 import android.hardware.display.DisplayManager; 61 import android.hardware.display.DisplayManager.DisplayListener; 62 import android.os.Build; 63 import android.os.Bundle; 64 import android.os.Handler; 65 import android.os.Looper; 66 import android.os.Parcel; 67 import android.os.ParcelFileDescriptor; 68 import android.os.SystemProperties; 69 import android.platform.test.annotations.AppModeSdkSandbox; 70 import android.platform.test.annotations.Presubmit; 71 import android.platform.test.annotations.RequiresFlagsDisabled; 72 import android.platform.test.annotations.RequiresFlagsEnabled; 73 import android.platform.test.flag.junit.CheckFlagsRule; 74 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 75 import android.provider.Settings; 76 import android.server.wm.WakeUpAndUnlockRule; 77 import android.text.TextUtils; 78 import android.util.ArraySet; 79 import android.util.DisplayMetrics; 80 import android.util.Log; 81 import android.view.Display; 82 import android.view.Display.HdrCapabilities; 83 import android.view.View; 84 import android.view.ViewGroup; 85 import android.view.WindowManager; 86 87 import androidx.test.ext.junit.runners.AndroidJUnit4; 88 import androidx.test.platform.app.InstrumentationRegistry; 89 import androidx.test.rule.ActivityTestRule; 90 91 import com.android.bedstead.harrier.DeviceState; 92 import com.android.bedstead.multiuser.annotations.RequireRunNotOnVisibleBackgroundNonProfileUser; 93 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 94 import com.android.compatibility.common.util.CddTest; 95 import com.android.compatibility.common.util.DisplayStateManager; 96 import com.android.compatibility.common.util.DisplayUtil; 97 import com.android.compatibility.common.util.MediaUtils; 98 import com.android.compatibility.common.util.PropertyUtil; 99 import com.android.compatibility.common.util.StateKeeperRule; 100 101 import com.google.common.truth.Truth; 102 103 import org.junit.After; 104 import org.junit.Before; 105 import org.junit.ClassRule; 106 import org.junit.Rule; 107 import org.junit.Test; 108 import org.junit.runner.RunWith; 109 110 import java.io.FileInputStream; 111 import java.io.InputStream; 112 import java.time.Duration; 113 import java.util.ArrayList; 114 import java.util.Arrays; 115 import java.util.Collections; 116 import java.util.List; 117 import java.util.Map; 118 import java.util.Objects; 119 import java.util.Optional; 120 import java.util.Random; 121 import java.util.Scanner; 122 import java.util.concurrent.CountDownLatch; 123 import java.util.concurrent.TimeUnit; 124 import java.util.concurrent.TimeoutException; 125 import java.util.concurrent.atomic.AtomicInteger; 126 import java.util.concurrent.locks.Condition; 127 import java.util.concurrent.locks.Lock; 128 import java.util.concurrent.locks.ReentrantLock; 129 import java.util.function.Predicate; 130 import java.util.stream.Collectors; 131 132 @RunWith(AndroidJUnit4.class) 133 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 134 public class DisplayTest extends TestBase { 135 private static final String TAG = "DisplayTest"; 136 137 @ClassRule @Rule 138 public static final DeviceState sDeviceState = new DeviceState(); 139 140 @Rule 141 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 142 143 // The CTS package brings up an overlay display on the target device (see AndroidTest.xml). 144 // The overlay display parameters must match the ones defined there which are 145 // 181x161/214 (wxh/dpi). It only matters that these values are different from any real 146 // display. 147 148 private static final int SECONDARY_DISPLAY_WIDTH = 181; 149 private static final int SECONDARY_DISPLAY_HEIGHT = 161; 150 private static final int SECONDARY_DISPLAY_DPI = 214; 151 private static final float SCALE_DENSITY_LOWER_BOUND = 152 (float)(SECONDARY_DISPLAY_DPI - 1) / DisplayMetrics.DENSITY_DEFAULT; 153 private static final float SCALE_DENSITY_UPPER_BOUND = 154 (float)(SECONDARY_DISPLAY_DPI + 1) / DisplayMetrics.DENSITY_DEFAULT; 155 // Matches com.android.internal.R.string.display_manager_overlay_display_name. 156 private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #"; 157 158 private static final int BRIGHTNESS_MAX = 255; 159 private static final float REFRESH_RATE_TOLERANCE = 0.001f; 160 161 private DisplayManager mDisplayManager; 162 private WindowManager mWindowManager; 163 private UiModeManager mUiModeManager; 164 private Context mContext; 165 private int mTestRunningUserId; 166 private ColorSpace[] mSupportedWideGamuts; 167 private Display mDefaultDisplay; 168 private int mInitialRefreshRateSwitchingType; 169 170 // To test display mode switches. 171 private TestPresentation mPresentation; 172 173 private UiAutomation mUiAutomation; 174 175 private static class DisplayModeState { 176 public final int mHeight; 177 public final int mWidth; 178 public final float mRefreshRate; 179 public final int[] mSupportedHdrTypes; 180 DisplayModeState(Display display)181 DisplayModeState(Display display) { 182 Display.Mode currentMode = display.getMode(); 183 mHeight = currentMode.getPhysicalHeight(); 184 mWidth = currentMode.getPhysicalWidth(); 185 mSupportedHdrTypes = currentMode.getSupportedHdrTypes(); 186 187 // Starting Android S the, the platform might throttle down 188 // applications frame rate to a divisor of the refresh rate instead if changing the 189 // physical display refresh rate. Applications should use 190 // {@link android.view.Display#getRefreshRate} to know their frame rate as opposed to 191 // {@link android.view.Display.Mode#getRefreshRate} that returns the physical display 192 // refresh rate. See 193 // {@link com.android.server.display.DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE} 194 // for more details. 195 mRefreshRate = display.getRefreshRate(); 196 } 197 DisplayModeState(Display.Mode mode)198 DisplayModeState(Display.Mode mode) { 199 mHeight = mode.getPhysicalHeight(); 200 mWidth = mode.getPhysicalWidth(); 201 mSupportedHdrTypes = mode.getSupportedHdrTypes(); 202 mRefreshRate = mode.getRefreshRate(); 203 } 204 205 @Override equals(Object obj)206 public boolean equals(Object obj) { 207 if (!(obj instanceof DisplayModeState)) { 208 return false; 209 } 210 211 DisplayModeState other = (DisplayModeState) obj; 212 return mHeight == other.mHeight 213 && mWidth == other.mWidth 214 && mRefreshRate == other.mRefreshRate 215 && Arrays.equals(mSupportedHdrTypes, other.mSupportedHdrTypes); 216 } 217 218 @Override hashCode()219 public int hashCode() { 220 return Objects.hash(mHeight, mWidth, mRefreshRate, Arrays.hashCode(mSupportedHdrTypes)); 221 } 222 223 @Override toString()224 public String toString() { 225 return new StringBuilder("{") 226 .append("width=").append(mWidth) 227 .append(", height=").append(mHeight) 228 .append(", fps=").append(mRefreshRate) 229 .append(", supportedHdrTypes=").append(Arrays.toString(mSupportedHdrTypes)) 230 .append("}") 231 .toString(); 232 } 233 } 234 235 @Rule 236 public ActivityTestRule<DisplayTestActivity> mDisplayTestActivity = 237 new ActivityTestRule<>( 238 DisplayTestActivity.class, 239 false /* initialTouchMode */, 240 false /* launchActivity */); 241 242 @Rule 243 public ActivityTestRule<RetainedDisplayTestActivity> mRetainedDisplayTestActivity = 244 new ActivityTestRule<>( 245 RetainedDisplayTestActivity.class, 246 false /* initialTouchMode */, 247 false /* launchActivity */); 248 249 @Rule(order = 0) 250 public WakeUpAndUnlockRule mWakeUpAndUnlockRule = new WakeUpAndUnlockRule(); 251 252 /** 253 * This rule adopts the Shell process permissions, needed because OVERRIDE_DISPLAY_MODE_REQUESTS 254 * and ACCESS_SURFACE_FLINGER are privileged permission. 255 */ 256 @Rule(order = 1) 257 public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( 258 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 259 Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, 260 Manifest.permission.ACCESS_SURFACE_FLINGER, 261 Manifest.permission.WRITE_SECURE_SETTINGS, 262 Manifest.permission.HDMI_CEC, 263 Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, 264 Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); 265 266 @Rule(order = 2) 267 public StateKeeperRule<DisplayStateManager.DisplayState> 268 mDisplayManagerStateKeeper = 269 new StateKeeperRule<>(new DisplayStateManager( 270 InstrumentationRegistry.getInstrumentation().getTargetContext())); 271 272 @Before setUp()273 public void setUp() throws Exception { 274 mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 275 276 mContext = getInstrumentation().getTargetContext(); 277 mTestRunningUserId = mContext.getUserId(); 278 assertTrue("Physical display is expected.", DisplayUtil.isDisplayConnected(mContext) 279 || MediaUtils.onCuttlefish()); 280 281 mDisplayManager = mContext.getSystemService(DisplayManager.class); 282 mWindowManager = mContext.getSystemService(WindowManager.class); 283 mUiModeManager = mContext.getSystemService(UiModeManager.class); 284 mDefaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY); 285 mSupportedWideGamuts = mDefaultDisplay.getSupportedWideColorGamut(); 286 287 addSecondaryDisplay(); 288 } 289 290 @After tearDown()291 public void tearDown() throws InterruptedException { 292 if (mDisplayManager != null) { 293 mDisplayManager.overrideHdrTypes(DEFAULT_DISPLAY, new int[]{}); 294 } 295 removeSecondaryDisplays(); 296 } 297 enableAppOps()298 private void enableAppOps() { 299 StringBuilder cmd = new StringBuilder(); 300 cmd.append("appops set --user " + mTestRunningUserId + " "); 301 cmd.append(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()); 302 cmd.append(" android:system_alert_window allow"); 303 InstrumentationRegistry.getInstrumentation().getUiAutomation() 304 .executeShellCommand(cmd.toString()); 305 306 StringBuilder query = new StringBuilder(); 307 query.append("appops get --user " + mTestRunningUserId + " "); 308 query.append(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()); 309 query.append(" android:system_alert_window"); 310 String queryStr = query.toString(); 311 312 String result = "No operations."; 313 while (result.contains("No operations")) { 314 ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation() 315 .getUiAutomation().executeShellCommand(queryStr); 316 InputStream inputStream = new FileInputStream(pfd.getFileDescriptor()); 317 result = convertStreamToString(inputStream); 318 } 319 } 320 convertStreamToString(InputStream is)321 private String convertStreamToString(InputStream is) { 322 try (java.util.Scanner s = new Scanner(is).useDelimiter("\\A")) { 323 return s.hasNext() ? s.next() : ""; 324 } 325 } 326 327 /** Check if the display is an overlay display, created by this test. */ isSecondaryDisplay(Display display)328 private boolean isSecondaryDisplay(Display display) { 329 return display.getType() == Display.TYPE_OVERLAY; 330 } 331 332 /** Get the overlay display, created by this test. */ getSecondaryDisplay(Display[] displays)333 private Display getSecondaryDisplay(Display[] displays) { 334 for (Display display : displays) { 335 if (isSecondaryDisplay(display)) { 336 return display; 337 } 338 } 339 return null; 340 } 341 addSecondaryDisplay()342 private void addSecondaryDisplay() throws InterruptedException { 343 final CountDownLatch signal = new CountDownLatch(1); 344 Handler handler = new Handler(Looper.getMainLooper()); 345 mDisplayManager.registerDisplayListener(new DisplayListener() { 346 @Override 347 public void onDisplayAdded(int displayId) { 348 if (getSecondaryDisplay(mDisplayManager.getDisplays()) != null) { 349 signal.countDown(); 350 } 351 } 352 353 @Override 354 public void onDisplayRemoved(int displayId) {} 355 356 @Override 357 public void onDisplayChanged(int displayId) {} 358 }, handler); 359 360 // Add a secondary display 361 mUiAutomation.executeShellCommand( 362 "settings put global overlay_display_devices 181x161/214|182x162/214" 363 +",fixed_content_mode"); 364 365 // Wait for the secondary display to become available 366 assertTrue(signal.await(20, TimeUnit.SECONDS)); 367 } 368 removeSecondaryDisplays()369 private void removeSecondaryDisplays() throws InterruptedException { 370 final CountDownLatch signal = new CountDownLatch(1); 371 Handler handler = new Handler(Looper.getMainLooper()); 372 mDisplayManager.registerDisplayListener(new DisplayListener() { 373 @Override 374 public void onDisplayAdded(int displayId) {} 375 376 @Override 377 public void onDisplayRemoved(int displayId) { 378 if (getSecondaryDisplay(mDisplayManager.getDisplays()) == null) { 379 signal.countDown(); 380 } 381 } 382 383 @Override 384 public void onDisplayChanged(int displayId) {} 385 }, handler); 386 387 // Remove secondary displays 388 mUiAutomation.executeShellCommand("settings delete global overlay_display_devices"); 389 390 // Wait for the change to go through 391 signal.await(20, TimeUnit.SECONDS); 392 } 393 394 /** 395 * Verify that the getDisplays method returns both a default and an overlay display. 396 */ 397 @Test testGetDisplays()398 public void testGetDisplays() { 399 Display[] displays = mDisplayManager.getDisplays(); 400 assertNotNull(displays); 401 assertTrue(2 <= displays.length); 402 boolean hasDefaultDisplay = false; 403 boolean hasSecondaryDisplay = false; 404 for (Display display : displays) { 405 if (display.getDisplayId() == DEFAULT_DISPLAY) { 406 hasDefaultDisplay = true; 407 } 408 if (isSecondaryDisplay(display)) { 409 hasSecondaryDisplay = true; 410 } 411 } 412 assertTrue(hasDefaultDisplay); 413 assertTrue(hasSecondaryDisplay); 414 } 415 416 @Test getDisplaysWithInvalidCategory_returnsEmptyArray()417 public void getDisplaysWithInvalidCategory_returnsEmptyArray() { 418 Display[] displays = mDisplayManager.getDisplays("InvalidDisplayCategory"); 419 420 assertThat(displays).isEmpty(); 421 } 422 423 /** 424 * Verify that the WindowManager returns the default display. 425 */ 426 @Presubmit 427 @Test testDefaultDisplay()428 public void testDefaultDisplay() { 429 assertEquals(DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId()); 430 } 431 432 /** 433 * Verify default display's HDR capability. 434 */ 435 @Test testDefaultDisplayHdrCapability()436 public void testDefaultDisplayHdrCapability() { 437 HdrCapabilities cap = mDefaultDisplay.getHdrCapabilities(); 438 int[] hdrTypes = cap.getSupportedHdrTypes(); 439 for (int type : hdrTypes) { 440 assertTrue(type >= 1 && type <= 4); 441 } 442 assertFalse(cap.getDesiredMaxLuminance() < -1.0f); 443 assertFalse(cap.getDesiredMinLuminance() < -1.0f); 444 assertFalse(cap.getDesiredMaxAverageLuminance() < -1.0f); 445 assertTrue(cap.getDesiredMinLuminance() <= cap.getDesiredMaxAverageLuminance()); 446 assertTrue(cap.getDesiredMaxAverageLuminance() <= cap.getDesiredMaxLuminance()); 447 if (hdrTypes.length > 0) { 448 assertTrue(mDefaultDisplay.isHdr()); 449 } else { 450 assertFalse(mDefaultDisplay.isHdr()); 451 } 452 } 453 454 /** 455 * Verifies that getHdrCapabilities filters out specified HDR types after 456 * setUserDisabledHdrTypes is called and setAreUserDisabledHdrTypes is false. 457 */ 458 @Test 459 public void 460 testGetHdrCapabilitiesWhenUserDisabledFormatsAreNotAllowedReturnsFilteredHdrTypes() 461 throws Exception { 462 overrideHdrTypes(); 463 waitUntil( 464 mDefaultDisplay, 465 mDefaultDisplay -> 466 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes().length == 4, 467 Duration.ofSeconds(5)); 468 469 mDisplayManager.setAreUserDisabledHdrTypesAllowed(false); 470 int[] emptyUserDisabledFormats = new int[] {}; 471 mDisplayManager.setUserDisabledHdrTypes(emptyUserDisabledFormats); 472 int[] expectedHdrTypes = new int[]{ 473 HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HDR10, 474 HdrCapabilities.HDR_TYPE_HLG, HdrCapabilities.HDR_TYPE_HDR10_PLUS}; 475 assertArrayEquals(expectedHdrTypes, 476 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes()); 477 478 int[] userDisabledHdrTypes = 479 {HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HLG}; 480 mDisplayManager.setUserDisabledHdrTypes(userDisabledHdrTypes); 481 expectedHdrTypes = new int[]{ 482 HdrCapabilities.HDR_TYPE_HDR10, 483 HdrCapabilities.HDR_TYPE_HDR10_PLUS}; 484 assertArrayEquals(expectedHdrTypes, 485 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes()); 486 487 mDisplayManager.setUserDisabledHdrTypes(emptyUserDisabledFormats); 488 expectedHdrTypes = new int[]{ 489 HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HDR10, 490 HdrCapabilities.HDR_TYPE_HLG, HdrCapabilities.HDR_TYPE_HDR10_PLUS}; 491 assertArrayEquals(expectedHdrTypes, 492 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes()); 493 } 494 495 /** 496 * Verifies that getHdrCapabilities doesn't filter out HDR types after 497 * setUserDisabledHdrTypes is called and setAreUserDisabledHdrTypes is true. 498 */ 499 @Test 500 public void 501 testGetHdrCapabilitiesWhenUserDisabledFormatsAreAllowedReturnsNonFilteredHdrTypes() 502 throws Exception { 503 overrideHdrTypes(); 504 waitUntil( 505 mDefaultDisplay, 506 mDefaultDisplay -> 507 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes().length == 4, 508 Duration.ofSeconds(5)); 509 510 mDisplayManager.setAreUserDisabledHdrTypesAllowed(true); 511 int[] userDisabledHdrTypes = 512 {HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HLG}; 513 mDisplayManager.setUserDisabledHdrTypes(userDisabledHdrTypes); 514 int[] expectedHdrTypes = new int[]{ 515 HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HDR10, 516 HdrCapabilities.HDR_TYPE_HLG, HdrCapabilities.HDR_TYPE_HDR10_PLUS}; 517 assertArrayEquals(expectedHdrTypes, 518 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes()); 519 520 int[] emptyUserDisabledFormats = {}; 521 mDisplayManager.setUserDisabledHdrTypes(emptyUserDisabledFormats); 522 assertArrayEquals(expectedHdrTypes, 523 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes()); 524 } 525 526 /** 527 * Verifies that if userDisabledFormats are not allowed, and are modified by 528 * setUserDisabledHdrTypes, the setting is persisted in Settings.Global. 529 */ 530 @Test 531 public void testSetUserDisabledHdrTypesStoresDisabledFormatsInSettings() throws Exception { 532 overrideHdrTypes(); 533 waitUntil( 534 mDefaultDisplay, 535 mDefaultDisplay -> 536 mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes().length == 4, 537 Duration.ofSeconds(5)); 538 539 mDisplayManager.setAreUserDisabledHdrTypesAllowed(false); 540 int[] emptyUserDisabledFormats = {}; 541 mDisplayManager.setUserDisabledHdrTypes(emptyUserDisabledFormats); 542 543 int[] userDisabledHdrTypes = 544 {HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HLG}; 545 mDisplayManager.setUserDisabledHdrTypes(userDisabledHdrTypes); 546 String userDisabledFormatsString = 547 Settings.Global.getString(mContext.getContentResolver(), 548 Settings.Global.USER_DISABLED_HDR_FORMATS); 549 int[] userDisabledFormats = Arrays.stream( 550 TextUtils.split(userDisabledFormatsString, ",")) 551 .mapToInt(Integer::parseInt).toArray(); 552 553 assertEquals(HdrCapabilities.HDR_TYPE_DOLBY_VISION, userDisabledFormats[0]); 554 assertEquals(HdrCapabilities.HDR_TYPE_HLG, userDisabledFormats[1]); 555 } 556 557 private void overrideHdrTypes() { 558 // TODO(b/347657922): HDR override not working for device without default HDR support. 559 // So run tests that require HDR override only for devices that support HDR by default. 560 assumeTrue(mDefaultDisplay.isHdr()); 561 mDisplayManager.overrideHdrTypes(DEFAULT_DISPLAY, new int[]{ 562 HdrCapabilities.HDR_TYPE_DOLBY_VISION, HdrCapabilities.HDR_TYPE_HDR10, 563 HdrCapabilities.HDR_TYPE_HLG, HdrCapabilities.HDR_TYPE_HDR10_PLUS}); 564 mDisplayManager.setAreUserDisabledHdrTypesAllowed(true); 565 } 566 567 private void waitUntil(Display display, Predicate<Display> pred, Duration maxWait) 568 throws Exception { 569 final int id = display.getDisplayId(); 570 final Lock lock = new ReentrantLock(); 571 final Condition displayChanged = lock.newCondition(); 572 DisplayListener listener = new DisplayListener() { 573 @Override 574 public void onDisplayChanged(int displayId) { 575 if (displayId != id) { 576 return; 577 } 578 lock.lock(); 579 try { 580 displayChanged.signal(); 581 } finally { 582 lock.unlock(); 583 } 584 } 585 @Override 586 public void onDisplayAdded(int displayId) {} 587 @Override 588 public void onDisplayRemoved(int displayId) {} 589 }; 590 Handler handler = new Handler(Looper.getMainLooper()); 591 mDisplayManager.registerDisplayListener(listener, handler); 592 long remainingNanos = maxWait.toNanos(); 593 lock.lock(); 594 try { 595 while (!pred.test(display)) { 596 if (remainingNanos <= 0L) { 597 throw new TimeoutException(); 598 } 599 remainingNanos = displayChanged.awaitNanos(remainingNanos); 600 } 601 } finally { 602 lock.unlock(); 603 } 604 } 605 606 /** 607 * Verify that there is a secondary display. 608 */ 609 @Test 610 public void testSecondaryDisplay() { 611 Display display = getSecondaryDisplay(mDisplayManager.getDisplays()); 612 assertNotNull(display); 613 assertTrue(DEFAULT_DISPLAY != display.getDisplayId()); 614 } 615 616 /** 617 * Test the properties of the secondary Display. 618 */ 619 @Test 620 public void testGetDisplayAttrs() { 621 Display display = getSecondaryDisplay(mDisplayManager.getDisplays()); 622 623 assertEquals(SECONDARY_DISPLAY_WIDTH, display.getWidth()); 624 assertEquals(SECONDARY_DISPLAY_HEIGHT, display.getHeight()); 625 626 Point outSize = new Point(); 627 display.getSize(outSize); 628 assertEquals(SECONDARY_DISPLAY_WIDTH, outSize.x); 629 assertEquals(SECONDARY_DISPLAY_HEIGHT, outSize.y); 630 631 assertEquals(0, display.getOrientation()); 632 633 assertEquals(PixelFormat.RGBA_8888, display.getPixelFormat()); 634 635 assertTrue(0 < display.getRefreshRate()); 636 637 assertTrue(display.getName().contains(OVERLAY_DISPLAY_NAME_PREFIX)); 638 639 assertFalse(display.isWideColorGamut()); 640 } 641 642 /** 643 * Test that the getMetrics method fills in correct values. 644 */ 645 @Test 646 public void testGetMetrics() { 647 testGetMetrics(mDisplayManager); 648 } 649 650 /** 651 * Tests getting metrics from the Activity context. 652 */ 653 @Test 654 public void testActivityContextGetMetrics() { 655 final Activity activity = launchActivity(mDisplayTestActivity); 656 final DisplayManager dm = 657 (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE); 658 testGetMetrics(dm); 659 } 660 661 public void testGetMetrics(DisplayManager manager) { 662 Display display = getSecondaryDisplay(manager.getDisplays()); 663 664 Point outSize = new Point(); 665 display.getRealSize(outSize); 666 667 DisplayMetrics outMetrics = new DisplayMetrics(); 668 outMetrics.setToDefaults(); 669 display.getMetrics(outMetrics); 670 671 assertEquals("Secondary display real size width is unexpected; height: " + outSize.y 672 + " name " + display.getName() + " id " + display.getDisplayId() 673 + " type " + display.getType(), SECONDARY_DISPLAY_WIDTH, outSize.x); 674 assertEquals(SECONDARY_DISPLAY_HEIGHT, outSize.y); 675 676 assertEquals("Secondary display width is unexpected; height: " + outMetrics.heightPixels 677 + " name " + display.getName() + " id " + display.getDisplayId() 678 + " type " + display.getType(), SECONDARY_DISPLAY_WIDTH, outMetrics.widthPixels); 679 assertEquals(SECONDARY_DISPLAY_HEIGHT, outMetrics.heightPixels); 680 681 // The scale is in [0.1, 3], and density is the scale factor. 682 assertTrue("Density is out of bounds: " + outMetrics.density + 683 " (Expected: [" + SCALE_DENSITY_LOWER_BOUND + ", " 684 + SCALE_DENSITY_UPPER_BOUND + "])", 685 SCALE_DENSITY_LOWER_BOUND <= outMetrics.density 686 && outMetrics.density <= SCALE_DENSITY_UPPER_BOUND); 687 assertTrue("ScaledDensity is out of bounds: " + outMetrics.scaledDensity + 688 " (Expected: [" + SCALE_DENSITY_LOWER_BOUND + ", " 689 + SCALE_DENSITY_UPPER_BOUND + "])", 690 SCALE_DENSITY_LOWER_BOUND <= outMetrics.scaledDensity 691 && outMetrics.scaledDensity <= SCALE_DENSITY_UPPER_BOUND); 692 693 assertEquals(SECONDARY_DISPLAY_DPI, outMetrics.densityDpi); 694 assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.xdpi, 0.0001f); 695 assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.ydpi, 0.0001f); 696 } 697 698 /** Test that the getFlags method returns expected flag bits set for the overlay display. */ 699 @Test 700 public void testFlags() { 701 Display display = getSecondaryDisplay(mDisplayManager.getDisplays()); 702 703 assertEquals(Display.FLAG_PRESENTATION | Display.FLAG_TRUSTED, display.getFlags()); 704 } 705 706 /** 707 * Test that a mode switch to every reported display mode is successful. 708 */ 709 @Test 710 public void testModeSwitchOnPrimaryDisplay() throws Exception { 711 // For VRR displays we might have multiple modes differ only by VSYNC - switching between 712 // them will be transparent for applications (VSYNC is hidden API) 713 // Also for VRR displays we itroduced synthetic modes - that only limit render rate 714 Map<DisplayModeState, Display.Mode> modes = Arrays 715 .stream(mDefaultDisplay.getSupportedModes()) 716 .filter(mode -> !mode.isSynthetic()) // filter out synthetic modes 717 // filter modes that differ only by VSYNC 718 .collect(Collectors.<Display.Mode, DisplayModeState, Display.Mode>toMap( 719 DisplayModeState::new, 720 mode -> mode, 721 (first, second) -> first)); 722 assumeTrue("Need two or more display modes to exercise switching.", modes.size() > 1); 723 724 try { 725 mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); 726 assertTrue(mDisplayManager.shouldAlwaysRespectAppRequestedMode()); 727 mInitialRefreshRateSwitchingType = 728 DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); 729 mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); 730 731 final DisplayTestActivity activity = launchActivity(mRetainedDisplayTestActivity); 732 733 // Create a deterministically shuffled list of display modes, which ends with the 734 // current active mode. We'll switch to the modes in this order. The active mode is last 735 // so we don't need an extra mode switch in case the test completes successfully. 736 Display.Mode activeMode = mDefaultDisplay.getMode(); 737 738 DisplayModeState activeModeState = new DisplayModeState(activeMode); 739 List<Display.Mode> modesList = new ArrayList<>(modes.size()); 740 for (Map.Entry<DisplayModeState, Display.Mode> mode : modes.entrySet()) { 741 if (!activeModeState.equals(mode.getKey())) { 742 modesList.add(mode.getValue()); 743 } 744 } 745 Random random = new Random(42); 746 Collections.shuffle(modesList, random); 747 modesList.add(activeMode); 748 749 for (Display.Mode mode : modesList) { 750 testSwitchToModeId(activity, mode); 751 } 752 } finally { 753 mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); 754 mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType); 755 } 756 } 757 758 /** 759 * Test that a mode switch to another display mode works when the requesting Activity 760 * is destroyed and re-created as part of the configuration change from the display mode. 761 */ 762 @Test 763 public void testModeSwitchOnPrimaryDisplayWithRestart() throws Exception { 764 final Display.Mode oldMode = mDefaultDisplay.getMode(); 765 final Optional<Display.Mode> newMode = Arrays.stream(mDefaultDisplay.getSupportedModes()) 766 .filter(x -> !getPhysicalSize(x).equals(getPhysicalSize(oldMode))) 767 .findFirst(); 768 assumeTrue("Modes with different sizes are not available", newMode.isPresent()); 769 770 try { 771 mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); 772 assertTrue(mDisplayManager.shouldAlwaysRespectAppRequestedMode()); 773 mInitialRefreshRateSwitchingType = 774 DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); 775 mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); 776 testSwitchToModeId(launchActivity(mDisplayTestActivity), newMode.get()); 777 } finally { 778 mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); 779 mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType); 780 } 781 } 782 783 private static Point getPhysicalSize(Display.Mode mode) { 784 return new Point(mode.getPhysicalWidth(), mode.getPhysicalHeight()); 785 } 786 787 private void testSwitchToModeId(DisplayTestActivity activity, Display.Mode targetMode) 788 throws Exception { 789 final DisplayModeState initialMode = new DisplayModeState(mDefaultDisplay); 790 Log.i(TAG, "Testing switching to mode " + targetMode + ". Current mode = " + initialMode); 791 792 final CountDownLatch changeSignal = new CountDownLatch(1); 793 final AtomicInteger changeCounter = new AtomicInteger(0); 794 final AtomicInteger changesToReachTargetMode = new AtomicInteger(0); 795 796 DisplayListener listener = new DisplayListener() { 797 private DisplayModeState mLastMode = initialMode; 798 private boolean mIsDesiredModeReached = false; 799 @Override 800 public void onDisplayAdded(int displayId) {} 801 802 @Override 803 public void onDisplayChanged(int displayId) { 804 if (displayId != mDefaultDisplay.getDisplayId()) { 805 return; 806 } 807 DisplayModeState newMode = new DisplayModeState(mDefaultDisplay); 808 if (mLastMode.equals(newMode)) { 809 // We assume this display change is caused by an external factor so it's 810 // unrelated. 811 return; 812 } 813 814 Log.i(TAG, "Switched mode from=" + mLastMode + " to=" + newMode); 815 changeCounter.incrementAndGet(); 816 817 if (targetMode.getPhysicalHeight() == newMode.mHeight 818 && targetMode.getPhysicalWidth() == newMode.mWidth 819 && Math.abs(targetMode.getRefreshRate() - newMode.mRefreshRate) 820 < REFRESH_RATE_TOLERANCE 821 && !mIsDesiredModeReached) { 822 mIsDesiredModeReached = true; 823 changeSignal.countDown(); 824 changesToReachTargetMode.set(changeCounter.get()); 825 } 826 827 mLastMode = newMode; 828 } 829 830 @Override 831 public void onDisplayRemoved(int displayId) {} 832 }; 833 834 Handler handler = new Handler(Looper.getMainLooper()); 835 mDisplayManager.registerDisplayListener(listener, handler); 836 837 final CountDownLatch presentationSignal = new CountDownLatch(1); 838 handler.post(() -> { 839 activity.setPreferredDisplayMode(targetMode); 840 presentationSignal.countDown(); 841 }); 842 843 assertTrue(presentationSignal.await(5, TimeUnit.SECONDS)); 844 845 // Wait until the display change is effective. 846 assertTrue(changeSignal.await(5, TimeUnit.SECONDS)); 847 DisplayModeState currentMode = new DisplayModeState(mDefaultDisplay); 848 assertEquals(targetMode.getPhysicalHeight(), currentMode.mHeight); 849 assertEquals(targetMode.getPhysicalWidth(), currentMode.mWidth); 850 assertEquals(targetMode.getRefreshRate(), currentMode.mRefreshRate, REFRESH_RATE_TOLERANCE); 851 assertArrayEquals(targetMode.getSupportedHdrTypes(), currentMode.mSupportedHdrTypes); 852 assertArrayEquals(mDefaultDisplay.getHdrCapabilities().getSupportedHdrTypes(), 853 currentMode.mSupportedHdrTypes); 854 855 856 boolean isResolutionSwitch = initialMode.mHeight != targetMode.getPhysicalHeight() 857 || initialMode.mWidth != targetMode.getPhysicalHeight(); 858 boolean isRefreshRateSwitch = 859 Math.abs(initialMode.mRefreshRate - targetMode.getRefreshRate()) 860 > REFRESH_RATE_TOLERANCE; 861 // When both resolution and refresh rate are changed the transition can happen with two 862 // mode switches: 863 // 1) When the frame rate vote is applied in 864 // java.com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded 865 // 2) When the DisplayManager policy is applied to RefreshRateConfigs in SurfaceFlinger. 866 // TODO(b/199895248) Expect only 1 mode change. 867 Truth.assertThat(changesToReachTargetMode.get()) 868 .isAtMost((isResolutionSwitch && isRefreshRateSwitch) ? 2 : 1); 869 870 // Make sure no more display mode changes are registered. 871 Thread.sleep(Duration.ofSeconds(3).toMillis()); 872 873 // When a resolution switch occurs the DisplayManager policy in RefreshRateConfigs 874 // is cleared and later reapplied. This may lead to two additional mode switches. 875 // TODO(b/200265160) Expect no changes. 876 Truth.assertThat(changeCounter.get() - changesToReachTargetMode.get()) 877 .isAtMost(isResolutionSwitch ? 2 : 0); 878 879 // Many TV apps use the vendor.display-size sysprop to detect the display size (although 880 // it's not an official API). In Android S the bugs which required this workaround were 881 // fixed and the sysprop should be either unset or should have the same value as the 882 // official API. The assertions are done after the delay above because on some 883 // devices the sysprop is not updated immediately after onDisplayChanged is called. 884 if (PropertyUtil.getVendorApiLevel() >= Build.VERSION_CODES.S) { 885 Point vendorDisplaySize = getVendorDisplaySize(); 886 if (vendorDisplaySize != null) { 887 assertEquals(targetMode.getPhysicalWidth(), vendorDisplaySize.x); 888 assertEquals(targetMode.getPhysicalHeight(), vendorDisplaySize.y); 889 } 890 } 891 892 mDisplayManager.unregisterDisplayListener(listener); 893 } 894 895 /** 896 * Tests that the mode-related attributes and methods work as expected. 897 */ 898 @Test testMode()899 public void testMode() { 900 Display display = getSecondaryDisplay(mDisplayManager.getDisplays()); 901 List<Display.Mode> modes = Arrays 902 .stream(display.getSupportedModes()) 903 .filter(mode -> !mode.isSynthetic()) // filter out synthetic modes 904 .toList(); 905 assertEquals(2, modes.size()); 906 Display.Mode mode = display.getMode(); 907 assertEquals(display.getSupportedModes()[0], mode); 908 assertEquals(SECONDARY_DISPLAY_WIDTH, mode.getPhysicalWidth()); 909 assertEquals(SECONDARY_DISPLAY_HEIGHT, mode.getPhysicalHeight()); 910 assertEquals(display.getRefreshRate(), mode.getRefreshRate(), 0.0001f); 911 } 912 913 /** 914 * Tests that getSupportedModes works as expected. 915 */ 916 @Test testGetSupportedModesOnDefaultDisplay()917 public void testGetSupportedModesOnDefaultDisplay() { 918 Display.Mode[] supportedModes = mDefaultDisplay.getSupportedModes(); 919 // We need to check that the graph defined by getAlternativeRefreshRates() is symmetric and 920 // transitive. 921 // For that reason we run a primitive Union-Find algorithm. In the end of the algorithm 922 // groups[i] == groups[j] iff supportedModes[i] and supportedModes[j] are in the same 923 // connected component. The complexity is O(N^2*M) where N is the number of modes and M is 924 // the max number of alternative refresh rates). This is okay as we expect a relatively 925 // small number of supported modes. 926 int[] groups = new int[supportedModes.length]; 927 for (int i = 0; i < groups.length; i++) { 928 groups[i] = i; 929 } 930 931 for (int i = 0; i < supportedModes.length; i++) { 932 Display.Mode supportedMode = supportedModes[i]; 933 for (float alternativeRate : supportedMode.getAlternativeRefreshRates()) { 934 assertTrue(alternativeRate != supportedMode.getRefreshRate()); 935 936 // The alternative exists. 937 int matchingModeIdx = -1; 938 for (int j = 0; j < supportedModes.length; j++) { 939 boolean matches = displayModeMatches(supportedModes[j], 940 supportedMode.getPhysicalWidth(), 941 supportedMode.getPhysicalHeight(), 942 alternativeRate); 943 if (matches) { 944 matchingModeIdx = j; 945 break; 946 } 947 } 948 String message = "Could not find alternative display mode with refresh rate " 949 + alternativeRate + " for " + supportedMode + ". All supported" 950 + " modes are " + Arrays.toString(supportedModes); 951 assertNotEquals(message, -1, matchingModeIdx); 952 953 // Merge the groups of i and matchingModeIdx 954 for (int k = 0; k < groups.length; k++) { 955 if (groups[k] == groups[matchingModeIdx]) { 956 groups[k] = groups[i]; 957 } 958 } 959 } 960 } 961 962 for (int i = 0; i < supportedModes.length; i++) { 963 for (int j = 0; j < supportedModes.length; j++) { 964 if (i != j && groups[i] == groups[j]) { 965 float fpsI = supportedModes[i].getRefreshRate(); 966 boolean iIsAlternativeToJ = false; 967 for (float alternatives : supportedModes[j].getAlternativeRefreshRates()) { 968 if (alternatives == fpsI) { 969 iIsAlternativeToJ = true; 970 break; 971 } 972 } 973 String message = "Expected " + supportedModes[i] + " to be listed as " 974 + "alternative refresh rate of " + supportedModes[j] + ". All supported" 975 + " modes are " + Arrays.toString(supportedModes); 976 assertTrue(message, iIsAlternativeToJ); 977 } 978 } 979 } 980 } 981 displayModeMatches(Display.Mode mode, int width, int height, float refreshRate)982 private boolean displayModeMatches(Display.Mode mode, int width, int height, 983 float refreshRate) { 984 return mode.getPhysicalWidth() == width && 985 mode.getPhysicalHeight() == height && 986 Float.floatToIntBits(mode.getRefreshRate()) == Float.floatToIntBits(refreshRate); 987 } 988 989 /** 990 * Tests that getMode() returns a mode which is in getSupportedModes(). 991 */ 992 @Test testActiveModeIsSupportedModesOnDefaultDisplay()993 public void testActiveModeIsSupportedModesOnDefaultDisplay() { 994 Display.Mode[] supportedModes = mDefaultDisplay.getSupportedModes(); 995 Display.Mode activeMode = mDefaultDisplay.getMode(); 996 boolean activeModeIsSupported = false; 997 for (Display.Mode mode : supportedModes) { 998 if (mode.equals(activeMode)) { 999 activeModeIsSupported = true; 1000 break; 1001 } 1002 } 1003 assertTrue(activeModeIsSupported); 1004 } 1005 1006 /** 1007 * Test that refresh rate switch app requests are correctly executed on a secondary display. 1008 * TODO(b/352630509): OverlayDisplay (i.e. SecondaryDisplay) does not support visible background 1009 * users at the moment, so skipping this test for secondary_user_on_secondary_display 1010 */ 1011 @Test 1012 @RequireRunNotOnVisibleBackgroundNonProfileUser testRefreshRateSwitchOnSecondaryDisplay()1013 public void testRefreshRateSwitchOnSecondaryDisplay() throws Exception { 1014 // Standalone VR devices globally ignore SYSTEM_ALERT_WINDOW via AppOps. 1015 // Skip this test, which depends on a Presentation SYSTEM_ALERT_WINDOW to pass. 1016 if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_VR_HEADSET) { 1017 return; 1018 } 1019 1020 enableAppOps(); 1021 final Display display = getSecondaryDisplay(mDisplayManager.getDisplays()); 1022 List<Display.Mode> modes = Arrays 1023 .stream(display.getSupportedModes()) 1024 .filter(mode -> !mode.isSynthetic()) // filter out synthetic modes 1025 .toList(); 1026 assertEquals(2, modes.size()); 1027 Display.Mode mode = display.getMode(); 1028 assertEquals(modes.get(0), mode); 1029 final Display.Mode newMode = modes.get(1); 1030 1031 Handler handler = new Handler(Looper.getMainLooper()); 1032 1033 // Register for display events. 1034 final CountDownLatch changeSignal = new CountDownLatch(1); 1035 mDisplayManager.registerDisplayListener(new DisplayListener() { 1036 @Override 1037 public void onDisplayAdded(int displayId) {} 1038 1039 @Override 1040 public void onDisplayChanged(int displayId) { 1041 if (displayId == display.getDisplayId() 1042 && display.getMode() != null 1043 && display.getMode().getModeId() == newMode.getModeId()) { 1044 changeSignal.countDown(); 1045 } 1046 } 1047 1048 @Override 1049 public void onDisplayRemoved(int displayId) {} 1050 }, handler); 1051 1052 // Show the presentation. 1053 final CountDownLatch presentationSignal = new CountDownLatch(1); 1054 handler.post(() -> { 1055 mPresentation = new TestPresentation( 1056 InstrumentationRegistry.getInstrumentation().getContext(), 1057 display, newMode.getModeId()); 1058 mPresentation.show(); 1059 presentationSignal.countDown(); 1060 }); 1061 assertTrue(presentationSignal.await(5, TimeUnit.SECONDS)); 1062 1063 // Wait until the display change is effective. 1064 assertTrue(changeSignal.await(5, TimeUnit.SECONDS)); 1065 1066 assertEquals(newMode, display.getMode()); 1067 handler.post(() -> mPresentation.dismiss()); 1068 } 1069 1070 /** 1071 * Verify that getColorSpace method returns the expected color space of the display. 1072 */ 1073 @Test testGetPreferredWideGamutColorSpace()1074 public void testGetPreferredWideGamutColorSpace() { 1075 final ColorSpace colorSpace = mDefaultDisplay.getPreferredWideGamutColorSpace(); 1076 1077 if (mDefaultDisplay.isWideColorGamut()) { 1078 assertFalse(colorSpace.isSrgb()); 1079 assertTrue(colorSpace.isWideGamut()); 1080 } else { 1081 assertNull(colorSpace); 1082 } 1083 } 1084 testGetOverlaySupportInternal(OverlayProperties overlayProperties)1085 private void testGetOverlaySupportInternal(OverlayProperties overlayProperties) { 1086 assertNotNull(overlayProperties); 1087 Parcel parcel = Parcel.obtain(); 1088 assertEquals(0, overlayProperties.describeContents()); 1089 overlayProperties.writeToParcel(parcel, 0); 1090 parcel.setDataPosition(0); 1091 OverlayProperties dest = OverlayProperties.CREATOR.createFromParcel(parcel); 1092 assertEquals(overlayProperties.isMixedColorSpacesSupported(), 1093 dest.isMixedColorSpacesSupported()); 1094 assertEquals(overlayProperties.isCombinationSupported( 1095 DataSpace.DATASPACE_SRGB, HardwareBuffer.RGBA_8888), 1096 dest.isCombinationSupported( 1097 DataSpace.DATASPACE_SRGB, HardwareBuffer.RGBA_8888)); 1098 if (android.hardware.flags.Flags.lutsApi()) { 1099 LutProperties[] lutProperties = overlayProperties.getLutProperties(); 1100 assertEquals(lutProperties, dest.getLutProperties()); 1101 if (lutProperties != null) { 1102 for (LutProperties properties : lutProperties) { 1103 assertTrue( 1104 properties.getDimension() == LutProperties.ONE_DIMENSION 1105 || properties.getDimension() == LutProperties.THREE_DIMENSION); 1106 assertTrue(properties.getSize() > 0); 1107 assertTrue(properties.getSamplingKeys().length > 0); 1108 } 1109 } 1110 } 1111 parcel.recycle(); 1112 } 1113 1114 @Test 1115 @RequiresFlagsEnabled(FLAG_OVERLAYPROPERTIES_CLASS_API) testGetOverlaySupportForPrimary()1116 public void testGetOverlaySupportForPrimary() { 1117 testGetOverlaySupportInternal(mDefaultDisplay.getOverlaySupport()); 1118 } 1119 1120 @Test 1121 @RequiresFlagsEnabled(FLAG_OVERLAYPROPERTIES_CLASS_API) testGetOverlaySupportForSecondary()1122 public void testGetOverlaySupportForSecondary() { 1123 Display secondaryDisplay = getSecondaryDisplay(mDisplayManager.getDisplays()); 1124 testGetOverlaySupportInternal(secondaryDisplay.getOverlaySupport()); 1125 assertTrue(secondaryDisplay.getOverlaySupport().isMixedColorSpacesSupported()); 1126 assertTrue(secondaryDisplay.getOverlaySupport() 1127 .isCombinationSupported(DataSpace.DATASPACE_SRGB, HardwareBuffer.RGBA_8888)); 1128 } 1129 1130 @Test testGetDeviceProductInfo()1131 public void testGetDeviceProductInfo() { 1132 DeviceProductInfo deviceProductInfo = mDefaultDisplay.getDeviceProductInfo(); 1133 assumeNotNull(deviceProductInfo); 1134 1135 assertNotNull(deviceProductInfo.getManufacturerPnpId()); 1136 1137 assertNotNull(deviceProductInfo.getProductId()); 1138 1139 final boolean isYearPresent = (deviceProductInfo.getModelYear() != -1) || 1140 (deviceProductInfo.getManufactureYear() != -1); 1141 assertTrue(isYearPresent); 1142 int year = deviceProductInfo.getModelYear() != -1 ? 1143 deviceProductInfo.getModelYear() : deviceProductInfo.getManufactureYear(); 1144 // Verify if the model year or manufacture year is greater than or equal to 1990. 1145 // This assumption is based on Section of 3.4.4 - Week and Year of Manufacture or Model Year 1146 // of VESA EDID STANDARD Version 1, Revision 4 1147 assertTrue(year >= 1990); 1148 1149 int week = deviceProductInfo.getManufactureWeek(); 1150 assertTrue(week == -1 || (week >= 1 && week <= 53)); 1151 1152 List<Integer> allowedConnectionToSinkValues = List.of( 1153 DeviceProductInfo.CONNECTION_TO_SINK_UNKNOWN, 1154 DeviceProductInfo.CONNECTION_TO_SINK_BUILT_IN, 1155 DeviceProductInfo.CONNECTION_TO_SINK_DIRECT, 1156 DeviceProductInfo.CONNECTION_TO_SINK_TRANSITIVE 1157 ); 1158 assertTrue( 1159 allowedConnectionToSinkValues.contains( 1160 deviceProductInfo.getConnectionToSinkType())); 1161 } 1162 1163 @Test testDeviceProductInfo()1164 public void testDeviceProductInfo() { 1165 DeviceProductInfo deviceProductInfo = new DeviceProductInfo( 1166 "DeviceName" /* name */, 1167 "TTL" /* manufacturePnpId */, 1168 "ProductId1" /* productId */, 1169 2000 /* modelYear */, 1170 DeviceProductInfo.CONNECTION_TO_SINK_DIRECT); 1171 1172 assertEquals("DeviceName", deviceProductInfo.getName()); 1173 assertEquals("TTL", deviceProductInfo.getManufacturerPnpId()); 1174 assertEquals("ProductId1", deviceProductInfo.getProductId()); 1175 assertEquals(2000, deviceProductInfo.getModelYear()); 1176 assertEquals(DeviceProductInfo.CONNECTION_TO_SINK_DIRECT, 1177 deviceProductInfo.getConnectionToSinkType()); 1178 } 1179 1180 @Test testFailBrightnessChangeWithoutPermission()1181 public void testFailBrightnessChangeWithoutPermission() throws Exception { 1182 final DisplayTestActivity activity = launchActivity(mDisplayTestActivity); 1183 final int originalValue = Settings.System.getInt(mContext.getContentResolver(), 1184 Settings.System.SCREEN_BRIGHTNESS, BRIGHTNESS_MAX); 1185 1186 try { 1187 final int valueToSet = originalValue > 128 ? 40 : 200; // sufficiently different value 1188 boolean wasSet = setBrightness(((float) valueToSet) / BRIGHTNESS_MAX); 1189 1190 assertFalse(wasSet); 1191 int newValue = Settings.System.getInt(mContext.getContentResolver(), 1192 Settings.System.SCREEN_BRIGHTNESS, BRIGHTNESS_MAX); 1193 assertEquals(originalValue, newValue); // verify that setting the new value failed. 1194 } finally { 1195 try { 1196 // Clean up just in case the test fails and we did actually manage to change the 1197 // brightness. 1198 Settings.System.putInt(mContext.getContentResolver(), 1199 Settings.System.SCREEN_BRIGHTNESS, originalValue); 1200 } catch (Exception e) { 1201 } 1202 } 1203 } 1204 1205 @Test testGetSupportedWideColorGamut_shouldNotBeNull()1206 public void testGetSupportedWideColorGamut_shouldNotBeNull() { 1207 assertNotNull(mSupportedWideGamuts); 1208 } 1209 1210 @Test testGetSupportWideColorGamut_displayIsWideColorGamut()1211 public void testGetSupportWideColorGamut_displayIsWideColorGamut() { 1212 final ColorSpace displayP3 = ColorSpace.get(ColorSpace.Named.DISPLAY_P3); 1213 final ColorSpace dciP3 = ColorSpace.get(ColorSpace.Named.DCI_P3); 1214 final List<ColorSpace> list = Arrays.asList(mSupportedWideGamuts); 1215 final boolean supportsWideGamut = mDefaultDisplay.isWideColorGamut() 1216 && mSupportedWideGamuts.length > 0; 1217 final boolean supportsP3 = list.contains(displayP3) || list.contains(dciP3); 1218 assertEquals(supportsWideGamut, supportsP3); 1219 } 1220 1221 @CddTest(requirement="7.1.1.1/H-0-2") 1222 @Test testRestrictedFramebufferSize()1223 public void testRestrictedFramebufferSize() { 1224 PackageManager packageManager = mContext.getPackageManager(); 1225 if (packageManager.hasSystemFeature(FEATURE_LEANBACK)) { 1226 // TV devices are allowed to restrict their framebuffer size. 1227 return; 1228 } 1229 1230 // Non-TV devices are not allowed by Android CDD to restrict their framebuffer size. 1231 String width = SystemProperties.get("ro.surface_flinger.max_graphics_width"); 1232 assertEquals("", width); 1233 String height = SystemProperties.get("ro.surface_flinger.max_graphics_height"); 1234 assertEquals("", height); 1235 } 1236 1237 @Test 1238 @RequiresFlagsEnabled(FLAG_ENABLE_HAS_ARR_SUPPORT) testHasArrSupport()1239 public void testHasArrSupport() { 1240 // TODO(b/365163281) Update the test case with more concrete behavior test 1241 mDefaultDisplay.hasArrSupport(); 1242 } 1243 1244 @Test 1245 @RequiresFlagsEnabled(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) testSuggestedFrameRate()1246 public void testSuggestedFrameRate() { 1247 final float normal = mDefaultDisplay.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL); 1248 final float high = mDefaultDisplay.getSuggestedFrameRate(FRAME_RATE_CATEGORY_HIGH); 1249 assertTrue(normal > 0); 1250 assertTrue(high > 0); 1251 assertTrue("FrameRateCategoryRate High should be greater than or equal to Normal", 1252 high >= normal); 1253 } 1254 1255 @Test (expected = IllegalArgumentException.class) 1256 @RequiresFlagsEnabled(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) testSuggestedFrameRate_throwsIllegalArgumentException()1257 public void testSuggestedFrameRate_throwsIllegalArgumentException() { 1258 mDefaultDisplay.getSuggestedFrameRate(2); 1259 } 1260 1261 @Test 1262 @RequiresFlagsEnabled(FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES) testGetSupportedRefreshRates()1263 public void testGetSupportedRefreshRates() { 1264 final float[] refreshRates = mDefaultDisplay.getSupportedRefreshRates(); 1265 final float epsilon = 0.001F; 1266 for (float refreshRateLegacy : mDefaultDisplay.getSupportedRefreshRatesLegacy()) { 1267 boolean isMatchFound = false; 1268 for (float refreshRate : refreshRates) { 1269 isMatchFound = Math.abs(refreshRateLegacy - refreshRate) <= epsilon; 1270 if (isMatchFound) { 1271 break; 1272 } 1273 } 1274 assertTrue("Failed to find the refresh rate " 1275 + refreshRateLegacy + " in " + Arrays.toString(refreshRates), isMatchFound); 1276 } 1277 } 1278 1279 @Test 1280 @RequiresFlagsEnabled(FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES) testGetSupportedRefreshRatesIsDivisorRate()1281 public void testGetSupportedRefreshRatesIsDivisorRate() { 1282 final float[] refreshRates = mDefaultDisplay.getSupportedRefreshRates(); 1283 ArraySet<Float> rates = new ArraySet<>(); 1284 Display.Mode defaultMode = mDefaultDisplay.getDefaultMode(); 1285 for (Display.Mode mode : mDefaultDisplay.getSupportedModes()) { 1286 if (mode.getPhysicalWidth() == defaultMode.getPhysicalWidth() 1287 && mode.getPhysicalHeight() == defaultMode.getPhysicalHeight()) { 1288 rates.add(mode.getVsyncRate()); 1289 } 1290 } 1291 final float epsilon = 0.001F; 1292 for (float refreshRate : refreshRates) { 1293 boolean isDivisorRateFound = false; 1294 for (float vsyncRate : rates) { 1295 final double result = vsyncRate / refreshRate; 1296 final double resultRounded = Math.round(vsyncRate / refreshRate); 1297 isDivisorRateFound = Math.abs(result - resultRounded) <= epsilon; 1298 if (isDivisorRateFound) { 1299 break; 1300 } 1301 } 1302 assertTrue("refreshRate " + refreshRate + " can not be achieved with " 1303 + rates, isDivisorRateFound); 1304 } 1305 } 1306 1307 @Test 1308 @RequiresFlagsDisabled(FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES) testGetSupportedRefreshRatesLegacy()1309 public void testGetSupportedRefreshRatesLegacy() { 1310 final float[] refreshRates = mDefaultDisplay.getSupportedRefreshRates(); 1311 final float[] refreshRateLegacy = mDefaultDisplay.getSupportedRefreshRatesLegacy(); 1312 assertArrayEquals(refreshRates, refreshRateLegacy, 0.0f); 1313 } 1314 1315 /** 1316 * Used to force mode changes on a display. 1317 * <p> 1318 * Note that due to limitations of the Presentation class, the modes must have the same size 1319 * otherwise the presentation will be automatically dismissed. 1320 */ 1321 private static final class TestPresentation extends Presentation { 1322 1323 private final int mModeId; 1324 TestPresentation(Context context, Display display, int modeId)1325 public TestPresentation(Context context, Display display, int modeId) { 1326 super(context, display); 1327 mModeId = modeId; 1328 } 1329 1330 @Override onCreate(Bundle savedInstanceState)1331 protected void onCreate(Bundle savedInstanceState) { 1332 super.onCreate(savedInstanceState); 1333 1334 View content = new View(getContext()); 1335 content.setLayoutParams(new ViewGroup.LayoutParams( 1336 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 1337 content.setBackgroundColor(Color.RED); 1338 setContentView(content); 1339 1340 WindowManager.LayoutParams params = getWindow().getAttributes(); 1341 params.preferredDisplayModeId = mModeId; 1342 params.setTitle("CtsTestPresentation"); 1343 getWindow().setAttributes(params); 1344 } 1345 1346 @Override cancel()1347 public void cancel() { 1348 // Ignore attempts to force cancel the presentation. This is going to happen when we 1349 // change the mode of the display since doing so will change the display metrics, which 1350 // Presentations don't yet support. Ignoring it means the Presentation will stay up and 1351 // the mode will stay changed until dismiss is called, preventing a race condition 1352 // between the test checking the mode of the display and the mode changing back to the 1353 // default because the requesting Presentation is no longer showing. 1354 } 1355 } 1356 1357 /** 1358 * Sets the brightness via the shell cmd. 1359 */ setBrightness(float value)1360 public boolean setBrightness(float value) throws Exception { 1361 Process process = Runtime.getRuntime().exec("cmd display set-brightness " + value); 1362 return 0 == process.waitFor(); 1363 } 1364 getVendorDisplaySize()1365 private Point getVendorDisplaySize() { 1366 String value = PropertyUtil.getProperty("vendor.display-size"); 1367 if (TextUtils.isEmpty(value)) { 1368 return null; 1369 } 1370 1371 String[] parts = value.split("x"); 1372 assertEquals(2, parts.length); 1373 return new Point(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); 1374 } 1375 } 1376