1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.display; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.assertArrayEquals; 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assume.assumeTrue; 31 import static org.mockito.ArgumentMatchers.anyFloat; 32 import static org.mockito.ArgumentMatchers.anyInt; 33 import static org.mockito.Mockito.mock; 34 import static org.mockito.Mockito.never; 35 import static org.mockito.Mockito.when; 36 37 import android.content.Context; 38 import android.content.res.Resources; 39 import android.content.res.TypedArray; 40 import android.graphics.Rect; 41 import android.os.Binder; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.Looper; 45 import android.view.Display; 46 import android.view.DisplayAddress; 47 import android.view.SurfaceControl; 48 import android.view.SurfaceControl.RefreshRateRange; 49 import android.view.SurfaceControl.RefreshRateRanges; 50 51 import androidx.test.filters.SmallTest; 52 import androidx.test.runner.AndroidJUnit4; 53 54 import com.android.dx.mockito.inline.extended.StaticMockitoSession; 55 import com.android.internal.R; 56 import com.android.server.LocalServices; 57 import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; 58 import com.android.server.display.mode.DisplayModeDirector; 59 import com.android.server.lights.LightsManager; 60 import com.android.server.lights.LogicalLight; 61 62 import com.google.common.truth.Truth; 63 64 import org.junit.After; 65 import org.junit.Assert; 66 import org.junit.Before; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.mockito.Mock; 70 import org.mockito.quality.Strictness; 71 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.LinkedList; 75 import java.util.concurrent.CountDownLatch; 76 import java.util.concurrent.TimeUnit; 77 78 79 @SmallTest 80 @RunWith(AndroidJUnit4.class) 81 public class LocalDisplayAdapterTest { 82 private static final Long DISPLAY_MODEL = Long.valueOf(0xAAAAAAAAL); 83 private static final int PORT_A = 0; 84 private static final int PORT_B = 0x80; 85 private static final int PORT_C = 0xFF; 86 private static final float REFRESH_RATE = 60f; 87 private static final RefreshRateRange REFRESH_RATE_RANGE = 88 new RefreshRateRange(REFRESH_RATE, REFRESH_RATE); 89 private static final RefreshRateRanges REFRESH_RATE_RANGES = 90 new RefreshRateRanges(REFRESH_RATE_RANGE, REFRESH_RATE_RANGE); 91 92 private static final long HANDLER_WAIT_MS = 100; 93 94 private static final int[] HDR_TYPES = new int[]{1, 2}; 95 96 private StaticMockitoSession mMockitoSession; 97 98 private LocalDisplayAdapter mAdapter; 99 100 @Mock 101 private DisplayManagerService.SyncRoot mMockedSyncRoot; 102 @Mock 103 private Context mMockedContext; 104 @Mock 105 private Resources mMockedResources; 106 @Mock 107 private LightsManager mMockedLightsManager; 108 @Mock 109 private LogicalLight mMockedBacklight; 110 111 private Handler mHandler; 112 113 private TestListener mListener = new TestListener(); 114 115 private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>(); 116 117 private Injector mInjector; 118 119 @Mock 120 private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy; 121 private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; 122 private static final int[] BACKLIGHT_RANGE = { 1, 255 }; 123 private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; 124 125 @Before setUp()126 public void setUp() throws Exception { 127 mMockitoSession = mockitoSession() 128 .initMocks(this) 129 .strictness(Strictness.LENIENT) 130 .startMocking(); 131 mHandler = new Handler(Looper.getMainLooper()); 132 doReturn(mMockedResources).when(mMockedContext).getResources(); 133 LocalServices.removeServiceForTest(LightsManager.class); 134 LocalServices.addService(LightsManager.class, mMockedLightsManager); 135 mInjector = new Injector(); 136 when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true); 137 mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler, 138 mListener, mInjector); 139 spyOn(mAdapter); 140 doReturn(mMockedContext).when(mAdapter).getOverlayContext(); 141 142 TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS); 143 when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits)) 144 .thenReturn(mockNitsRange); 145 when(mMockedResources.getIntArray(R.array.config_screenBrightnessBacklight)) 146 .thenReturn(BACKLIGHT_RANGE); 147 when(mMockedResources.getFloat(com.android.internal.R.dimen 148 .config_screenBrightnessSettingMinimumFloat)) 149 .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[0]); 150 when(mMockedResources.getFloat(com.android.internal.R.dimen 151 .config_screenBrightnessSettingMaximumFloat)) 152 .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]); 153 when(mMockedResources.getStringArray(R.array.config_displayUniqueIdArray)) 154 .thenReturn(new String[]{}); 155 TypedArray mockArray = mock(TypedArray.class); 156 when(mockArray.length()).thenReturn(0); 157 when(mMockedResources.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray)) 158 .thenReturn(mockArray); 159 when(mMockedResources.obtainTypedArray(R.array.config_waterfallCutoutArray)) 160 .thenReturn(mockArray); 161 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerRadiusArray)) 162 .thenReturn(mockArray); 163 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerTopRadiusArray)) 164 .thenReturn(mockArray); 165 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerBottomRadiusArray)) 166 .thenReturn(mockArray); 167 when(mMockedResources.obtainTypedArray( 168 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) 169 .thenReturn(mockArray); 170 when(mMockedResources.getIntArray( 171 com.android.internal.R.array.config_autoBrightnessLevels)) 172 .thenReturn(new int[]{}); 173 when(mMockedResources.obtainTypedArray(R.array.config_displayShapeArray)) 174 .thenReturn(mockArray); 175 when(mMockedResources.obtainTypedArray(R.array.config_builtInDisplayIsRoundArray)) 176 .thenReturn(mockArray); 177 when(mMockedResources.getIntArray( 178 com.android.internal.R.array.config_brightnessThresholdsOfPeakRefreshRate)) 179 .thenReturn(new int[]{}); 180 when(mMockedResources.getIntArray( 181 com.android.internal.R.array.config_ambientThresholdsOfPeakRefreshRate)) 182 .thenReturn(new int[]{}); 183 when(mMockedResources.getIntArray( 184 com.android.internal.R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate)) 185 .thenReturn(new int[]{}); 186 when(mMockedResources.getIntArray( 187 com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate)) 188 .thenReturn(new int[]{}); 189 } 190 191 @After tearDown()192 public void tearDown() { 193 if (mMockitoSession != null) { 194 mMockitoSession.finishMocking(); 195 } 196 } 197 198 /** 199 * Confirm that display is marked as private when it is listed in 200 * com.android.internal.R.array.config_localPrivateDisplayPorts. 201 */ 202 @Test testPrivateDisplay()203 public void testPrivateDisplay() throws Exception { 204 setUpDisplay(new FakeDisplay(PORT_A)); 205 setUpDisplay(new FakeDisplay(PORT_B)); 206 setUpDisplay(new FakeDisplay(PORT_C)); 207 updateAvailableDisplays(); 208 209 doReturn(new int[]{ PORT_B }).when(mMockedResources) 210 .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts); 211 mAdapter.registerLocked(); 212 213 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 214 215 // This should be public 216 assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), 217 PORT_A, false); 218 // This should be private 219 assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), 220 PORT_B, true); 221 // This should be public 222 assertDisplayPrivateFlag(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), 223 PORT_C, false); 224 } 225 226 @Test testSupportedDisplayModesGetOverriddenWhenDisplayIsUpdated()227 public void testSupportedDisplayModesGetOverriddenWhenDisplayIsUpdated() 228 throws InterruptedException { 229 SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 0); 230 displayMode.supportedHdrTypes = new int[0]; 231 FakeDisplay display = new FakeDisplay(PORT_A, new SurfaceControl.DisplayMode[]{displayMode}, 232 0, 0); 233 setUpDisplay(display); 234 updateAvailableDisplays(); 235 mAdapter.registerLocked(); 236 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 237 238 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 239 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 240 Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes; 241 Assert.assertEquals(1, supportedModes.length); 242 Assert.assertEquals(0, supportedModes[0].getSupportedHdrTypes().length); 243 244 displayMode.supportedHdrTypes = new int[]{3, 2}; 245 display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{displayMode}; 246 setUpDisplay(display); 247 mInjector.getTransmitter().sendHotplug(display, true); 248 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 249 250 displayDevice = mListener.changedDisplays.get(0); 251 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 252 supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes; 253 254 Assert.assertEquals(1, supportedModes.length); 255 assertArrayEquals(new int[]{2, 3}, supportedModes[0].getSupportedHdrTypes()); 256 } 257 258 /** 259 * Confirm that all local displays are public when config_localPrivateDisplayPorts is empty. 260 */ 261 @Test testPublicDisplaysForNoConfigLocalPrivateDisplayPorts()262 public void testPublicDisplaysForNoConfigLocalPrivateDisplayPorts() throws Exception { 263 setUpDisplay(new FakeDisplay(PORT_A)); 264 setUpDisplay(new FakeDisplay(PORT_C)); 265 updateAvailableDisplays(); 266 267 // config_localPrivateDisplayPorts is null 268 mAdapter.registerLocked(); 269 270 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 271 272 // This should be public 273 assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), 274 PORT_A, false); 275 // This should be public 276 assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), 277 PORT_C, false); 278 } 279 assertDisplayPrivateFlag( DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate)280 private static void assertDisplayPrivateFlag( 281 DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate) { 282 final DisplayAddress.Physical address = (DisplayAddress.Physical) info.address; 283 assertNotNull(address); 284 assertEquals(expectedPort, address.getPort()); 285 assertEquals(DISPLAY_MODEL, address.getModel()); 286 assertEquals(shouldBePrivate, (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0); 287 } 288 289 /** 290 * Confirm that external display uses physical density. 291 */ 292 @Test testDpiValues()293 public void testDpiValues() throws Exception { 294 // needs default one always 295 setUpDisplay(new FakeDisplay(PORT_A)); 296 setUpDisplay(new FakeDisplay(PORT_B)); 297 updateAvailableDisplays(); 298 mAdapter.registerLocked(); 299 300 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 301 302 assertDisplayDpi( 303 mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, 100, 100, 304 16000); 305 assertDisplayDpi( 306 mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, 100, 100, 307 16000); 308 } 309 310 private static class DisplayModeWrapper { 311 public SurfaceControl.DisplayMode mode; 312 public float[] expectedAlternativeRefreshRates; 313 DisplayModeWrapper(SurfaceControl.DisplayMode mode, float[] expectedAlternativeRefreshRates)314 DisplayModeWrapper(SurfaceControl.DisplayMode mode, 315 float[] expectedAlternativeRefreshRates) { 316 this.mode = mode; 317 this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates; 318 } 319 } 320 321 /** 322 * Updates the <code>display</code> using the given <code>modes</code> and then checks if the 323 * <code>expectedAlternativeRefreshRates</code> are present for each of the 324 * <code>modes</code>. 325 */ testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] wrappedModes)326 private void testAlternativeRefreshRatesCommon(FakeDisplay display, 327 DisplayModeWrapper[] wrappedModes) 328 throws InterruptedException { 329 // Update the display. 330 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length]; 331 for (int i = 0; i < wrappedModes.length; i++) { 332 modes[i] = wrappedModes[i].mode; 333 } 334 display.dynamicInfo.supportedDisplayModes = modes; 335 setUpDisplay(display); 336 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 337 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 338 assertTrue(mListener.traversalRequested); 339 assertThat(mListener.changedDisplays.size()).isGreaterThan(0); 340 341 // Verify the supported modes are updated accordingly. 342 DisplayDevice displayDevice = 343 mListener.changedDisplays.get(mListener.changedDisplays.size() - 1); 344 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 345 Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes; 346 assertThat(supportedModes.length).isEqualTo(modes.length); 347 348 for (int i = 0; i < wrappedModes.length; i++) { 349 assertModeIsSupported(supportedModes, modes[i], 350 wrappedModes[i].expectedAlternativeRefreshRates); 351 } 352 } 353 354 @Test testAfterDisplayChange_AlternativeRefreshRatesAreUpdated()355 public void testAfterDisplayChange_AlternativeRefreshRatesAreUpdated() throws Exception { 356 FakeDisplay display = new FakeDisplay(PORT_A); 357 setUpDisplay(display); 358 updateAvailableDisplays(); 359 mAdapter.registerLocked(); 360 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 361 362 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 363 new DisplayModeWrapper( 364 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{24f, 50f}), 365 new DisplayModeWrapper( 366 createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{24f, 60f}), 367 new DisplayModeWrapper( 368 createFakeDisplayMode(2, 1920, 1080, 24f, 0), new float[]{50f, 60f}), 369 new DisplayModeWrapper( 370 createFakeDisplayMode(3, 3840, 2160, 60f, 0), new float[]{24f, 50f}), 371 new DisplayModeWrapper( 372 createFakeDisplayMode(4, 3840, 2160, 50f, 0), new float[]{24f, 60f}), 373 new DisplayModeWrapper( 374 createFakeDisplayMode(5, 3840, 2160, 24f, 0), new float[]{50f, 60f}), 375 }); 376 377 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 378 new DisplayModeWrapper( 379 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{50f}), 380 new DisplayModeWrapper( 381 createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{60f}), 382 new DisplayModeWrapper( 383 createFakeDisplayMode(2, 1920, 1080, 24f, 1), new float[0]), 384 new DisplayModeWrapper( 385 createFakeDisplayMode(3, 3840, 2160, 60f, 2), new float[0]), 386 new DisplayModeWrapper( 387 createFakeDisplayMode(4, 3840, 2160, 50f, 3), new float[]{24f}), 388 new DisplayModeWrapper( 389 createFakeDisplayMode(5, 3840, 2160, 24f, 3), new float[]{50f}), 390 }); 391 392 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 393 new DisplayModeWrapper( 394 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[0]), 395 new DisplayModeWrapper( 396 createFakeDisplayMode(1, 1920, 1080, 50f, 1), new float[0]), 397 new DisplayModeWrapper( 398 createFakeDisplayMode(2, 1920, 1080, 24f, 2), new float[0]), 399 new DisplayModeWrapper( 400 createFakeDisplayMode(3, 3840, 2160, 60f, 3), new float[0]), 401 new DisplayModeWrapper( 402 createFakeDisplayMode(4, 3840, 2160, 50f, 4), new float[0]), 403 new DisplayModeWrapper( 404 createFakeDisplayMode(5, 3840, 2160, 24f, 5), new float[0]), 405 }); 406 } 407 408 @Test testAfterDisplayChange_DefaultDisplayModeIsUpdated()409 public void testAfterDisplayChange_DefaultDisplayModeIsUpdated() throws Exception { 410 SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f); 411 SurfaceControl.DisplayMode[] modes = 412 new SurfaceControl.DisplayMode[]{displayMode}; 413 FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, displayMode.refreshRate); 414 setUpDisplay(display); 415 updateAvailableDisplays(); 416 mAdapter.registerLocked(); 417 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 418 419 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 420 assertThat(mListener.changedDisplays).isEmpty(); 421 422 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 423 0).getDisplayDeviceInfoLocked(); 424 425 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 426 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 427 428 Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 429 assertThat(matches(defaultMode, displayMode)).isTrue(); 430 431 // Set the display mode to an unsupported mode 432 SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 1920, 1080, 120f); 433 mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked( 434 new Display.Mode(displayMode2.width, displayMode2.height, 435 displayMode2.refreshRate)); 436 updateAvailableDisplays(); 437 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 438 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 439 assertThat(matches(defaultMode, displayMode2)).isFalse(); 440 441 // Change the display 442 modes = new SurfaceControl.DisplayMode[]{displayMode, displayMode2}; 443 display.dynamicInfo.supportedDisplayModes = modes; 444 setUpDisplay(display); 445 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 446 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 447 448 assertTrue(mListener.traversalRequested); 449 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 450 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 451 452 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 453 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 454 displayDeviceInfo = mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(); 455 456 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 457 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 458 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode2); 459 460 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 461 assertThat(matches(defaultMode, displayMode2)).isTrue(); 462 } 463 464 @Test testAfterDisplayChange_DisplayModesAreUpdated()465 public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception { 466 SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f); 467 SurfaceControl.DisplayMode[] modes = 468 new SurfaceControl.DisplayMode[]{displayMode}; 469 FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, displayMode.refreshRate); 470 setUpDisplay(display); 471 updateAvailableDisplays(); 472 mAdapter.registerLocked(); 473 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 474 475 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 476 assertThat(mListener.changedDisplays).isEmpty(); 477 478 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 479 0).getDisplayDeviceInfoLocked(); 480 481 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 482 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 483 484 Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 485 assertThat(matches(defaultMode, displayMode)).isTrue(); 486 487 Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 488 assertThat(matches(activeMode, displayMode)).isTrue(); 489 490 // Change the display 491 SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f); 492 modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo}; 493 display.dynamicInfo.supportedDisplayModes = modes; 494 display.dynamicInfo.activeDisplayModeId = 1; 495 setUpDisplay(display); 496 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 497 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 498 499 assertTrue(mListener.traversalRequested); 500 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 501 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 502 503 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 504 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 505 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 506 507 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 508 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 509 assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo); 510 511 activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 512 assertThat(matches(activeMode, addedDisplayInfo)).isTrue(); 513 514 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 515 assertThat(matches(defaultMode, addedDisplayInfo)).isTrue(); 516 } 517 518 @Test testAfterDisplayChange_ActiveModeIsUpdated()519 public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception { 520 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ 521 createFakeDisplayMode(0, 1920, 1080, 60f), 522 createFakeDisplayMode(1, 1920, 1080, 50f) 523 }; 524 FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0, 60f); 525 setUpDisplay(display); 526 updateAvailableDisplays(); 527 mAdapter.registerLocked(); 528 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 529 530 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 531 assertThat(mListener.changedDisplays).isEmpty(); 532 533 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 534 .getDisplayDeviceInfoLocked(); 535 536 Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 537 assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); 538 539 // Change the display 540 display.dynamicInfo.activeDisplayModeId = 1; 541 setUpDisplay(display); 542 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 543 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 544 545 assertTrue(mListener.traversalRequested); 546 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 547 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 548 549 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 550 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 551 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 552 553 activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 554 assertThat(activeMode.matches(1920, 1080, 50f)).isTrue(); 555 } 556 557 @Test testAfterDisplayChange_RenderFrameRateIsUpdated()558 public void testAfterDisplayChange_RenderFrameRateIsUpdated() throws Exception { 559 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ 560 createFakeDisplayMode(0, 1920, 1080, 60f), 561 }; 562 FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0, 563 /* renderFrameRate */30f); 564 setUpDisplay(display); 565 updateAvailableDisplays(); 566 mAdapter.registerLocked(); 567 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 568 569 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 570 assertThat(mListener.changedDisplays).isEmpty(); 571 572 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 573 .getDisplayDeviceInfoLocked(); 574 575 Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 576 assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); 577 assertEquals(Float.floatToIntBits(30f), 578 Float.floatToIntBits(displayDeviceInfo.renderFrameRate)); 579 580 // Change the render frame rate 581 display.dynamicInfo.renderFrameRate = 60f; 582 setUpDisplay(display); 583 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 584 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 585 586 assertTrue(mListener.traversalRequested); 587 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 588 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 589 590 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 591 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 592 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 593 594 activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 595 assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); 596 assertEquals(Float.floatToIntBits(60f), 597 Float.floatToIntBits(displayDeviceInfo.renderFrameRate)); 598 } 599 600 @Test testAfterDisplayChange_HdrCapabilitiesAreUpdated()601 public void testAfterDisplayChange_HdrCapabilitiesAreUpdated() throws Exception { 602 FakeDisplay display = new FakeDisplay(PORT_A); 603 Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0], 604 1000, 1000, 0); 605 display.dynamicInfo.hdrCapabilities = initialHdrCapabilities; 606 setUpDisplay(display); 607 updateAvailableDisplays(); 608 mAdapter.registerLocked(); 609 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 610 611 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 612 assertThat(mListener.changedDisplays).isEmpty(); 613 614 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 615 0).getDisplayDeviceInfoLocked(); 616 617 assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(initialHdrCapabilities); 618 619 // Change the display 620 Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities( 621 new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0); 622 display.dynamicInfo.hdrCapabilities = changedHdrCapabilities; 623 setUpDisplay(display); 624 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 625 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 626 627 assertTrue(mListener.traversalRequested); 628 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 629 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 630 631 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 632 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 633 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 634 635 assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(changedHdrCapabilities); 636 } 637 638 @Test testAfterDisplayChange_AllmSupportIsUpdated()639 public void testAfterDisplayChange_AllmSupportIsUpdated() throws Exception { 640 FakeDisplay display = new FakeDisplay(PORT_A); 641 display.dynamicInfo.autoLowLatencyModeSupported = true; 642 setUpDisplay(display); 643 updateAvailableDisplays(); 644 mAdapter.registerLocked(); 645 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 646 647 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 648 assertThat(mListener.changedDisplays).isEmpty(); 649 650 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 651 .getDisplayDeviceInfoLocked(); 652 653 assertThat(displayDeviceInfo.allmSupported).isTrue(); 654 655 // Change the display 656 display.dynamicInfo.autoLowLatencyModeSupported = false; 657 setUpDisplay(display); 658 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 659 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 660 661 assertTrue(mListener.traversalRequested); 662 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 663 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 664 665 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 666 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 667 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 668 669 assertThat(displayDeviceInfo.allmSupported).isFalse(); 670 } 671 672 @Test testAfterDisplayStateChanges_committedSetAfterState()673 public void testAfterDisplayStateChanges_committedSetAfterState() throws Exception { 674 FakeDisplay display = new FakeDisplay(PORT_A); 675 setUpDisplay(display); 676 updateAvailableDisplays(); 677 mAdapter.registerLocked(); 678 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 679 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 680 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 681 682 // Turn off. 683 Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_OFF, 0, 684 0); 685 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 686 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 687 mListener.changedDisplays.clear(); 688 assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF); 689 assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isNotEqualTo( 690 Display.STATE_OFF); 691 verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF); 692 693 // Execute powerstate change. 694 changeStateRunnable.run(); 695 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 696 697 698 // Verify that committed triggered a new change event and is set correctly. 699 verify(mSurfaceControlProxy, never()).setDisplayPowerMode(display.token, Display.STATE_OFF); 700 // We expect at least 1 update for the state change, but 701 // could get a second update for the initial brightness change if a nits mapping 702 // is available 703 assertThat(mListener.changedDisplays.size()).isAnyOf(1, 2); 704 assertThat(displayDevice.getDisplayDeviceInfoLocked().state).isEqualTo(Display.STATE_OFF); 705 assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState).isEqualTo( 706 Display.STATE_OFF); 707 } 708 709 @Test testAfterDisplayChange_GameContentTypeSupportIsUpdated()710 public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception { 711 FakeDisplay display = new FakeDisplay(PORT_A); 712 display.dynamicInfo.gameContentTypeSupported = true; 713 setUpDisplay(display); 714 updateAvailableDisplays(); 715 mAdapter.registerLocked(); 716 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 717 718 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 719 assertThat(mListener.changedDisplays).isEmpty(); 720 721 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 722 .getDisplayDeviceInfoLocked(); 723 724 assertThat(displayDeviceInfo.gameContentTypeSupported).isTrue(); 725 726 // Change the display 727 display.dynamicInfo.gameContentTypeSupported = false; 728 setUpDisplay(display); 729 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 730 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 731 732 assertTrue(mListener.traversalRequested); 733 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 734 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 735 736 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 737 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 738 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 739 740 assertThat(displayDeviceInfo.gameContentTypeSupported).isFalse(); 741 } 742 743 @Test testAfterDisplayChange_ColorModesAreUpdated()744 public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception { 745 FakeDisplay display = new FakeDisplay(PORT_A); 746 final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709}; 747 display.dynamicInfo.supportedColorModes = initialColorModes; 748 setUpDisplay(display); 749 updateAvailableDisplays(); 750 mAdapter.registerLocked(); 751 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 752 753 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 754 assertThat(mListener.changedDisplays).isEmpty(); 755 756 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 757 .getDisplayDeviceInfoLocked(); 758 759 assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_BT709); 760 assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(initialColorModes); 761 762 // Change the display 763 final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT}; 764 display.dynamicInfo.supportedColorModes = changedColorModes; 765 setUpDisplay(display); 766 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 767 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 768 769 assertTrue(mListener.traversalRequested); 770 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 771 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 772 773 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 774 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 775 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 776 777 assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_DEFAULT); 778 assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes); 779 } 780 781 @Test testDisplayChange_withStaleDesiredDisplayModeSpecs()782 public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception { 783 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ 784 createFakeDisplayMode(0, 1920, 1080, 60f), 785 createFakeDisplayMode(1, 1920, 1080, 50f) 786 }; 787 final int activeMode = 0; 788 FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode, 60f); 789 display.desiredDisplayModeSpecs.defaultMode = 1; 790 791 setUpDisplay(display); 792 updateAvailableDisplays(); 793 mAdapter.registerLocked(); 794 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 795 796 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 797 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 798 799 int baseModeId = Arrays.stream(displayDevice.getDisplayDeviceInfoLocked().supportedModes) 800 .filter(mode -> mode.getRefreshRate() == 60f) 801 .findFirst() 802 .get() 803 .getModeId(); 804 805 displayDevice.setDesiredDisplayModeSpecsLocked( 806 new DisplayModeDirector.DesiredDisplayModeSpecs( 807 /*baseModeId*/ baseModeId, 808 /*allowGroupSwitching*/ false, 809 REFRESH_RATE_RANGES, REFRESH_RATE_RANGES 810 )); 811 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 812 verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, 813 new SurfaceControl.DesiredDisplayModeSpecs( 814 /* baseModeId */ 0, 815 /* allowGroupSwitching */ false, 816 REFRESH_RATE_RANGES, REFRESH_RATE_RANGES 817 )); 818 819 // Change the display 820 display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ 821 createFakeDisplayMode(2, 1920, 1080, 60f) 822 }; 823 display.dynamicInfo.activeDisplayModeId = 2; 824 // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't crash. 825 display.desiredDisplayModeSpecs.defaultMode = 1; 826 827 setUpDisplay(display); 828 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 829 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 830 831 assertTrue(mListener.traversalRequested); 832 833 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 834 835 baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId(); 836 837 // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device 838 displayDevice.setDesiredDisplayModeSpecsLocked( 839 new DisplayModeDirector.DesiredDisplayModeSpecs( 840 /*baseModeId*/ baseModeId, 841 /*allowGroupSwitching*/ false, 842 REFRESH_RATE_RANGES, REFRESH_RATE_RANGES 843 )); 844 845 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 846 847 // Verify that this will reapply the desired modes. 848 verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, 849 new SurfaceControl.DesiredDisplayModeSpecs( 850 /* baseModeId */ 2, 851 /* allowGroupSwitching */ false, 852 REFRESH_RATE_RANGES, REFRESH_RATE_RANGES 853 )); 854 } 855 856 @Test testBacklightAdapter_withSurfaceControlSupport()857 public void testBacklightAdapter_withSurfaceControlSupport() { 858 final Binder displayToken = new Binder(); 859 860 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(true); 861 862 // Test as default display 863 BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, 864 mSurfaceControlProxy); 865 ba.setBacklight(0.514f, 100f, 0.614f, 500f); 866 verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f, 100f, 0.614f, 500f); 867 868 // Test as not default display 869 BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, 870 mSurfaceControlProxy); 871 ba2.setBacklight(0.323f, 101f, 0.723f, 601f); 872 verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f, 101f, 0.723f, 601f); 873 } 874 875 @Test testBacklightAdapter_withoutSourceControlSupport_defaultDisplay()876 public void testBacklightAdapter_withoutSourceControlSupport_defaultDisplay() { 877 final Binder displayToken = new Binder(); 878 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); 879 doReturn(mMockedBacklight).when(mMockedLightsManager) 880 .getLight(LightsManager.LIGHT_ID_BACKLIGHT); 881 882 BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, 883 mSurfaceControlProxy); 884 ba.setBacklight(1f, 1f, 0.123f, 1f); 885 verify(mMockedBacklight).setBrightness(0.123f); 886 } 887 888 @Test testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay()889 public void testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay() { 890 final Binder displayToken = new Binder(); 891 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); 892 doReturn(mMockedBacklight).when(mMockedLightsManager) 893 .getLight(LightsManager.LIGHT_ID_BACKLIGHT); 894 895 BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, 896 mSurfaceControlProxy); 897 ba.setBacklight(0.456f, 1f, 1f, 1f); 898 899 // Adapter does not forward any brightness in this case. 900 verify(mMockedBacklight, never()).setBrightness(anyFloat()); 901 } 902 903 @Test testGetSystemPreferredDisplayMode()904 public void testGetSystemPreferredDisplayMode() throws Exception { 905 SurfaceControl.DisplayMode displayMode1 = createFakeDisplayMode(0, 1920, 1080, 60f); 906 // system preferred mode 907 SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 3840, 2160, 60f); 908 // user preferred mode 909 SurfaceControl.DisplayMode displayMode3 = createFakeDisplayMode(2, 1920, 1080, 30f); 910 911 SurfaceControl.DisplayMode[] modes = 912 new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, displayMode3}; 913 FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, 1); 914 setUpDisplay(display); 915 updateAvailableDisplays(); 916 mAdapter.registerLocked(); 917 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 918 919 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 920 assertThat(mListener.changedDisplays).isEmpty(); 921 922 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 923 0).getDisplayDeviceInfoLocked(); 924 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 925 Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 926 assertThat(matches(defaultMode, displayMode1)).isTrue(); 927 928 // Set the user preferred display mode 929 mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked( 930 new Display.Mode( 931 displayMode3.width, displayMode3.height, displayMode3.refreshRate)); 932 updateAvailableDisplays(); 933 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 934 displayDeviceInfo = mListener.addedDisplays.get( 935 0).getDisplayDeviceInfoLocked(); 936 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 937 assertThat(matches(defaultMode, displayMode3)).isTrue(); 938 939 // clear the user preferred mode 940 mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(null); 941 updateAvailableDisplays(); 942 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 943 displayDeviceInfo = mListener.addedDisplays.get( 944 0).getDisplayDeviceInfoLocked(); 945 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 946 assertThat(matches(defaultMode, displayMode2)).isTrue(); 947 948 // Change the display and add new system preferred mode 949 SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3, 2340, 1080, 20f); 950 modes = new SurfaceControl.DisplayMode[]{ 951 displayMode1, displayMode2, displayMode3, addedDisplayInfo}; 952 display.dynamicInfo.supportedDisplayModes = modes; 953 display.dynamicInfo.preferredBootDisplayMode = 3; 954 setUpDisplay(display); 955 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 956 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 957 958 assertTrue(mListener.traversalRequested); 959 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 960 assertThat(mListener.changedDisplays.size()).isEqualTo(3); 961 962 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 963 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 964 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 965 966 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 967 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode1); 968 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode2); 969 assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo); 970 971 assertThat(matches(displayDevice.getSystemPreferredDisplayModeLocked(), addedDisplayInfo)) 972 .isTrue(); 973 } 974 975 @Test testHdrSdrRatio_notifiesOnChange()976 public void testHdrSdrRatio_notifiesOnChange() throws Exception { 977 FakeDisplay display = new FakeDisplay(PORT_A); 978 setUpDisplay(display); 979 updateAvailableDisplays(); 980 mAdapter.registerLocked(); 981 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 982 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 983 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 984 985 // Turn on / initialize 986 assumeTrue(displayDevice.getDisplayDeviceConfig().hasSdrToHdrRatioSpline()); 987 Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0, 988 0); 989 changeStateRunnable.run(); 990 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 991 mListener.changedDisplays.clear(); 992 993 assertEquals(1.0f, displayDevice.getDisplayDeviceInfoLocked().hdrSdrRatio, 0.001f); 994 995 // HDR time! 996 Runnable goHdrRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 1f, 997 0); 998 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 999 // Display state didn't change, no listeners should have happened 1000 assertThat(mListener.changedDisplays.size()).isEqualTo(0); 1001 1002 // Execute hdr change. 1003 goHdrRunnable.run(); 1004 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1005 // Display state didn't change, expect to only get the HDR/SDR ratio change notification 1006 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 1007 1008 final float expectedRatio = DISPLAY_RANGE_NITS[1] / DISPLAY_RANGE_NITS[0]; 1009 assertEquals(expectedRatio, displayDevice.getDisplayDeviceInfoLocked().hdrSdrRatio, 1010 0.001f); 1011 } 1012 1013 @Test test_getDisplayDeviceInfoLocked_internalDisplay_usesCutoutAndCorners()1014 public void test_getDisplayDeviceInfoLocked_internalDisplay_usesCutoutAndCorners() 1015 throws Exception { 1016 setupCutoutAndRoundedCorners(); 1017 FakeDisplay display = new FakeDisplay(PORT_A); 1018 display.info.isInternal = true; 1019 setUpDisplay(display); 1020 updateAvailableDisplays(); 1021 mAdapter.registerLocked(); 1022 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1023 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 1024 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 1025 1026 // Turn on / initialize 1027 Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0, 1028 0); 1029 changeStateRunnable.run(); 1030 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1031 mListener.changedDisplays.clear(); 1032 1033 DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked(); 1034 1035 assertThat(info.displayCutout).isNotNull(); 1036 assertThat(info.displayCutout.getBoundingRectTop()).isEqualTo(new Rect(507, 33, 573, 99)); 1037 assertThat(info.roundedCorners).isNotNull(); 1038 assertThat(info.roundedCorners.getRoundedCorner(0).getRadius()).isEqualTo(5); 1039 } 1040 test_getDisplayDeviceInfoLocked_externalDisplay_doesNotUseCutoutOrCorners()1041 @Test public void test_getDisplayDeviceInfoLocked_externalDisplay_doesNotUseCutoutOrCorners() 1042 throws Exception { 1043 setupCutoutAndRoundedCorners(); 1044 FakeDisplay display = new FakeDisplay(PORT_A); 1045 display.info.isInternal = false; 1046 setUpDisplay(display); 1047 updateAvailableDisplays(); 1048 mAdapter.registerLocked(); 1049 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1050 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 1051 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 1052 1053 // Turn on / initialize 1054 Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0, 1055 0); 1056 changeStateRunnable.run(); 1057 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1058 mListener.changedDisplays.clear(); 1059 1060 DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked(); 1061 1062 assertThat(info.displayCutout).isNull(); 1063 assertThat(info.roundedCorners).isNull(); 1064 } 1065 setupCutoutAndRoundedCorners()1066 private void setupCutoutAndRoundedCorners() { 1067 String sampleCutout = "M 507,66\n" 1068 + "a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0\n" 1069 + "Z\n" 1070 + "@left\n"; 1071 // Setup some default cutout 1072 when(mMockedResources.getString( 1073 com.android.internal.R.string.config_mainBuiltInDisplayCutout)) 1074 .thenReturn(sampleCutout); 1075 when(mMockedResources.getDimensionPixelSize( 1076 com.android.internal.R.dimen.rounded_corner_radius)).thenReturn(5); 1077 } 1078 assertDisplayDpi(DisplayDeviceInfo info, int expectedPort, float expectedXdpi, float expectedYDpi, int expectedDensityDpi)1079 private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort, 1080 float expectedXdpi, 1081 float expectedYDpi, 1082 int expectedDensityDpi) { 1083 final DisplayAddress.Physical physical = (DisplayAddress.Physical) info.address; 1084 assertNotNull(physical); 1085 assertEquals(expectedPort, physical.getPort()); 1086 assertEquals(expectedXdpi, info.xDpi, 0.01); 1087 assertEquals(expectedYDpi, info.yDpi, 0.01); 1088 assertEquals(expectedDensityDpi, info.densityDpi); 1089 } 1090 getModeById(DisplayDeviceInfo displayDeviceInfo, int modeId)1091 private Display.Mode getModeById(DisplayDeviceInfo displayDeviceInfo, int modeId) { 1092 return Arrays.stream(displayDeviceInfo.supportedModes) 1093 .filter(mode -> mode.getModeId() == modeId) 1094 .findFirst() 1095 .get(); 1096 } 1097 assertModeIsSupported(Display.Mode[] supportedModes, SurfaceControl.DisplayMode mode)1098 private void assertModeIsSupported(Display.Mode[] supportedModes, 1099 SurfaceControl.DisplayMode mode) { 1100 assertThat(Arrays.stream(supportedModes).anyMatch( 1101 x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue(); 1102 } 1103 assertModeIsSupported(Display.Mode[] supportedModes, SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates)1104 private void assertModeIsSupported(Display.Mode[] supportedModes, 1105 SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) { 1106 float[] sortedAlternativeRates = 1107 Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length); 1108 Arrays.sort(sortedAlternativeRates); 1109 1110 String message = "Expected " + mode + " with alternativeRefreshRates = " 1111 + Arrays.toString(alternativeRefreshRates) + " to be in list of supported modes = " 1112 + Arrays.toString(supportedModes); 1113 Truth.assertWithMessage(message) 1114 .that(Arrays.stream(supportedModes) 1115 .anyMatch(x -> x.matches(mode.width, mode.height, mode.refreshRate) 1116 && Arrays.equals(x.getAlternativeRefreshRates(), sortedAlternativeRates))) 1117 .isTrue(); 1118 } 1119 1120 1121 private static class FakeDisplay { 1122 public final DisplayAddress.Physical address; 1123 public final IBinder token = new Binder(); 1124 public final SurfaceControl.StaticDisplayInfo info; 1125 public SurfaceControl.DynamicDisplayInfo dynamicInfo = 1126 new SurfaceControl.DynamicDisplayInfo(); 1127 { 1128 dynamicInfo.supportedColorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; 1129 dynamicInfo.hdrCapabilities = new Display.HdrCapabilities(new int[0], 1130 1000, 1000, 0); 1131 } 1132 1133 public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs = 1134 new SurfaceControl.DesiredDisplayModeSpecs( 1135 /* defaultMode */ 0, 1136 /* allowGroupSwitching */ false, 1137 REFRESH_RATE_RANGES, REFRESH_RATE_RANGES 1138 ); 1139 FakeDisplay(int port)1140 private FakeDisplay(int port) { 1141 address = createDisplayAddress(port); 1142 info = createFakeDisplayInfo(); 1143 dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ 1144 createFakeDisplayMode(0, 800, 600, 60f) 1145 }; 1146 dynamicInfo.activeDisplayModeId = 0; 1147 } 1148 FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode, float renderFrameRate)1149 private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode, 1150 float renderFrameRate) { 1151 address = createDisplayAddress(port); 1152 info = createFakeDisplayInfo(); 1153 dynamicInfo.supportedDisplayModes = modes; 1154 dynamicInfo.activeDisplayModeId = activeMode; 1155 dynamicInfo.renderFrameRate = renderFrameRate; 1156 } 1157 FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode, int preferredMode)1158 private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode, 1159 int preferredMode) { 1160 address = createDisplayAddress(port); 1161 info = createFakeDisplayInfo(); 1162 dynamicInfo.supportedDisplayModes = modes; 1163 dynamicInfo.activeDisplayModeId = activeMode; 1164 dynamicInfo.preferredBootDisplayMode = preferredMode; 1165 } 1166 1167 } 1168 setUpDisplay(FakeDisplay display)1169 private void setUpDisplay(FakeDisplay display) { 1170 mAddresses.add(display.address); 1171 when(mSurfaceControlProxy.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())) 1172 .thenReturn(display.token); 1173 when(mSurfaceControlProxy.getStaticDisplayInfo(display.address.getPhysicalDisplayId())) 1174 .thenReturn(display.info); 1175 when(mSurfaceControlProxy.getDynamicDisplayInfo(display.address.getPhysicalDisplayId())) 1176 .thenReturn(display.dynamicInfo); 1177 when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)) 1178 .thenReturn(display.desiredDisplayModeSpecs); 1179 } 1180 updateAvailableDisplays()1181 private void updateAvailableDisplays() { 1182 long[] ids = new long[mAddresses.size()]; 1183 int i = 0; 1184 for (DisplayAddress.Physical address : mAddresses) { 1185 ids[i] = address.getPhysicalDisplayId(); 1186 i++; 1187 } 1188 when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids); 1189 } 1190 createDisplayAddress(int port)1191 private static DisplayAddress.Physical createDisplayAddress(int port) { 1192 return DisplayAddress.fromPortAndModel(port, DISPLAY_MODEL); 1193 } 1194 createFakeDisplayInfo()1195 private static SurfaceControl.StaticDisplayInfo createFakeDisplayInfo() { 1196 final SurfaceControl.StaticDisplayInfo info = new SurfaceControl.StaticDisplayInfo(); 1197 info.density = 100; 1198 return info; 1199 } 1200 createFakeDisplayMode(int id, int width, int height, float refreshRate)1201 private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, 1202 float refreshRate) { 1203 return createFakeDisplayMode(id, width, height, refreshRate, /* group */ 0); 1204 } 1205 createFakeDisplayMode(int id, int width, int height, float refreshRate, int group)1206 private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, 1207 float refreshRate, int group) { 1208 final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode(); 1209 mode.id = id; 1210 mode.width = width; 1211 mode.height = height; 1212 mode.refreshRate = refreshRate; 1213 mode.xDpi = 100; 1214 mode.yDpi = 100; 1215 mode.group = group; 1216 mode.supportedHdrTypes = HDR_TYPES; 1217 return mode; 1218 } 1219 waitForHandlerToComplete(Handler handler, long waitTimeMs)1220 private static void waitForHandlerToComplete(Handler handler, long waitTimeMs) 1221 throws InterruptedException { 1222 final CountDownLatch fence = new CountDownLatch(1); 1223 handler.post(fence::countDown); 1224 assertTrue(fence.await(waitTimeMs, TimeUnit.MILLISECONDS)); 1225 } 1226 1227 private class HotplugTransmitter { 1228 private final Handler mHandler; 1229 private final LocalDisplayAdapter.DisplayEventListener mListener; 1230 HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener)1231 HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener) { 1232 mHandler = new Handler(looper); 1233 mListener = listener; 1234 } 1235 sendHotplug(FakeDisplay display, boolean connected)1236 public void sendHotplug(FakeDisplay display, boolean connected) 1237 throws InterruptedException { 1238 mHandler.post(() -> mListener.onHotplug(/* timestampNanos = */ 0, 1239 display.address.getPhysicalDisplayId(), connected)); 1240 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 1241 } 1242 } 1243 1244 private class Injector extends LocalDisplayAdapter.Injector { 1245 private HotplugTransmitter mTransmitter; 1246 @Override setDisplayEventListenerLocked(Looper looper, LocalDisplayAdapter.DisplayEventListener listener)1247 public void setDisplayEventListenerLocked(Looper looper, 1248 LocalDisplayAdapter.DisplayEventListener listener) { 1249 mTransmitter = new HotplugTransmitter(looper, listener); 1250 } 1251 getTransmitter()1252 public HotplugTransmitter getTransmitter() { 1253 return mTransmitter; 1254 } 1255 1256 @Override getSurfaceControlProxy()1257 public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { 1258 return mSurfaceControlProxy; 1259 } 1260 1261 // Instead of using DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay) 1262 // we should use DisplayDeviceConfig.create(context, isFirstDisplay) for the test to ensure 1263 // that real device DisplayDeviceConfig is not loaded for FakeDisplay and we are getting 1264 // consistent behaviour. Please also note that context passed to this method, is 1265 // mMockContext and values will be loaded from mMockResources. 1266 @Override createDisplayDeviceConfig(Context context, long physicalDisplayId, boolean isFirstDisplay)1267 public DisplayDeviceConfig createDisplayDeviceConfig(Context context, 1268 long physicalDisplayId, boolean isFirstDisplay) { 1269 return DisplayDeviceConfig.create(context, isFirstDisplay); 1270 } 1271 } 1272 1273 private class TestListener implements DisplayAdapter.Listener { 1274 public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>(); 1275 public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>(); 1276 public boolean traversalRequested = false; 1277 1278 @Override onDisplayDeviceEvent(DisplayDevice device, int event)1279 public void onDisplayDeviceEvent(DisplayDevice device, int event) { 1280 if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED) { 1281 addedDisplays.add(device); 1282 } else if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED) { 1283 changedDisplays.add(device); 1284 } 1285 } 1286 1287 @Override onTraversalRequested()1288 public void onTraversalRequested() { 1289 traversalRequested = true; 1290 } 1291 } 1292 createFloatTypedArray(float[] vals)1293 private TypedArray createFloatTypedArray(float[] vals) { 1294 TypedArray mockArray = mock(TypedArray.class); 1295 when(mockArray.length()).thenAnswer(invocation -> { 1296 return vals.length; 1297 }); 1298 when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { 1299 final float def = (float) invocation.getArguments()[1]; 1300 if (vals == null) { 1301 return def; 1302 } 1303 int idx = (int) invocation.getArguments()[0]; 1304 if (idx >= 0 && idx < vals.length) { 1305 return vals[idx]; 1306 } else { 1307 return def; 1308 } 1309 }); 1310 return mockArray; 1311 } 1312 matches(Display.Mode a, SurfaceControl.DisplayMode b)1313 private boolean matches(Display.Mode a, SurfaceControl.DisplayMode b) { 1314 return a.getPhysicalWidth() == b.width && a.getPhysicalHeight() == b.height 1315 && Float.floatToIntBits(a.getRefreshRate()) == Float.floatToIntBits(b.refreshRate); 1316 } 1317 } 1318