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.assertEquals; 27 import static org.junit.Assert.assertNotNull; 28 import static org.junit.Assert.assertTrue; 29 import static org.mockito.ArgumentMatchers.anyFloat; 30 import static org.mockito.ArgumentMatchers.anyInt; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.when; 34 35 import android.content.Context; 36 import android.content.res.Resources; 37 import android.content.res.TypedArray; 38 import android.hardware.display.DisplayManagerInternal.RefreshRateRange; 39 import android.os.Binder; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.view.Display; 44 import android.view.DisplayAddress; 45 import android.view.SurfaceControl; 46 47 import androidx.test.filters.SmallTest; 48 import androidx.test.runner.AndroidJUnit4; 49 50 import com.android.dx.mockito.inline.extended.StaticMockitoSession; 51 import com.android.internal.R; 52 import com.android.server.LocalServices; 53 import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; 54 import com.android.server.lights.LightsManager; 55 import com.android.server.lights.LogicalLight; 56 57 import com.google.common.truth.Truth; 58 59 import org.junit.After; 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 import org.mockito.Mock; 64 import org.mockito.quality.Strictness; 65 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.LinkedList; 69 70 71 @SmallTest 72 @RunWith(AndroidJUnit4.class) 73 public class LocalDisplayAdapterTest { 74 private static final Long DISPLAY_MODEL = Long.valueOf(0xAAAAAAAAL); 75 private static final int PORT_A = 0; 76 private static final int PORT_B = 0x80; 77 private static final int PORT_C = 0xFF; 78 79 private static final long HANDLER_WAIT_MS = 100; 80 81 private StaticMockitoSession mMockitoSession; 82 83 private LocalDisplayAdapter mAdapter; 84 85 @Mock 86 private DisplayManagerService.SyncRoot mMockedSyncRoot; 87 @Mock 88 private Context mMockedContext; 89 @Mock 90 private Resources mMockedResources; 91 @Mock 92 private LightsManager mMockedLightsManager; 93 @Mock 94 private LogicalLight mMockedBacklight; 95 96 private Handler mHandler; 97 98 private TestListener mListener = new TestListener(); 99 100 private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>(); 101 102 private Injector mInjector; 103 104 @Mock 105 private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy; 106 private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; 107 private static final int[] BACKLIGHT_RANGE = { 1, 255 }; 108 private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; 109 110 @Before setUp()111 public void setUp() throws Exception { 112 mMockitoSession = mockitoSession() 113 .initMocks(this) 114 .strictness(Strictness.LENIENT) 115 .startMocking(); 116 mHandler = new Handler(Looper.getMainLooper()); 117 doReturn(mMockedResources).when(mMockedContext).getResources(); 118 LocalServices.removeServiceForTest(LightsManager.class); 119 LocalServices.addService(LightsManager.class, mMockedLightsManager); 120 mInjector = new Injector(); 121 mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler, 122 mListener, mInjector); 123 spyOn(mAdapter); 124 doReturn(mMockedContext).when(mAdapter).getOverlayContext(); 125 126 TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS); 127 when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits)) 128 .thenReturn(mockNitsRange); 129 when(mMockedResources.getIntArray(R.array.config_screenBrightnessBacklight)) 130 .thenReturn(BACKLIGHT_RANGE); 131 when(mMockedResources.getFloat(com.android.internal.R.dimen 132 .config_screenBrightnessSettingMinimumFloat)) 133 .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[0]); 134 when(mMockedResources.getFloat(com.android.internal.R.dimen 135 .config_screenBrightnessSettingMaximumFloat)) 136 .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]); 137 when(mMockedResources.getStringArray(R.array.config_displayUniqueIdArray)) 138 .thenReturn(new String[]{}); 139 TypedArray mockArray = mock(TypedArray.class); 140 when(mockArray.length()).thenReturn(0); 141 when(mMockedResources.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray)) 142 .thenReturn(mockArray); 143 when(mMockedResources.obtainTypedArray(R.array.config_waterfallCutoutArray)) 144 .thenReturn(mockArray); 145 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerRadiusArray)) 146 .thenReturn(mockArray); 147 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerTopRadiusArray)) 148 .thenReturn(mockArray); 149 when(mMockedResources.obtainTypedArray(R.array.config_roundedCornerBottomRadiusArray)) 150 .thenReturn(mockArray); 151 } 152 153 @After tearDown()154 public void tearDown() { 155 if (mMockitoSession != null) { 156 mMockitoSession.finishMocking(); 157 } 158 } 159 160 /** 161 * Confirm that display is marked as private when it is listed in 162 * com.android.internal.R.array.config_localPrivateDisplayPorts. 163 */ 164 @Test testPrivateDisplay()165 public void testPrivateDisplay() throws Exception { 166 setUpDisplay(new FakeDisplay(PORT_A)); 167 setUpDisplay(new FakeDisplay(PORT_B)); 168 setUpDisplay(new FakeDisplay(PORT_C)); 169 updateAvailableDisplays(); 170 171 doReturn(new int[]{ PORT_B }).when(mMockedResources) 172 .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts); 173 mAdapter.registerLocked(); 174 175 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 176 177 // This should be public 178 assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), 179 PORT_A, false); 180 // This should be public 181 assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), 182 PORT_B, true); 183 // This should be public 184 assertDisplayPrivateFlag(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), 185 PORT_C, false); 186 } 187 188 /** 189 * Confirm that all local displays are public when config_localPrivateDisplayPorts is empty. 190 */ 191 @Test testPublicDisplaysForNoConfigLocalPrivateDisplayPorts()192 public void testPublicDisplaysForNoConfigLocalPrivateDisplayPorts() throws Exception { 193 setUpDisplay(new FakeDisplay(PORT_A)); 194 setUpDisplay(new FakeDisplay(PORT_C)); 195 updateAvailableDisplays(); 196 197 // config_localPrivateDisplayPorts is null 198 mAdapter.registerLocked(); 199 200 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 201 202 // This should be public 203 assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), 204 PORT_A, false); 205 // This should be public 206 assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), 207 PORT_C, false); 208 } 209 assertDisplayPrivateFlag( DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate)210 private static void assertDisplayPrivateFlag( 211 DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate) { 212 final DisplayAddress.Physical address = (DisplayAddress.Physical) info.address; 213 assertNotNull(address); 214 assertEquals(expectedPort, address.getPort()); 215 assertEquals(DISPLAY_MODEL, address.getModel()); 216 assertEquals(shouldBePrivate, (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0); 217 } 218 219 /** 220 * Confirm that external display uses physical density. 221 */ 222 @Test testDpiValues()223 public void testDpiValues() throws Exception { 224 // needs default one always 225 setUpDisplay(new FakeDisplay(PORT_A)); 226 setUpDisplay(new FakeDisplay(PORT_B)); 227 updateAvailableDisplays(); 228 mAdapter.registerLocked(); 229 230 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 231 232 assertDisplayDpi( 233 mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, 100, 100, 234 16000); 235 assertDisplayDpi( 236 mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, 100, 100, 237 16000); 238 } 239 240 private static class DisplayModeWrapper { 241 public SurfaceControl.DisplayMode mode; 242 public float[] expectedAlternativeRefreshRates; 243 DisplayModeWrapper(SurfaceControl.DisplayMode mode, float[] expectedAlternativeRefreshRates)244 DisplayModeWrapper(SurfaceControl.DisplayMode mode, 245 float[] expectedAlternativeRefreshRates) { 246 this.mode = mode; 247 this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates; 248 } 249 } 250 251 /** 252 * Updates the <code>display</code> using the given <code>modes</code> and then checks if the 253 * <code>expectedAlternativeRefreshRates</code> are present for each of the 254 * <code>modes</code>. 255 */ testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] wrappedModes)256 private void testAlternativeRefreshRatesCommon(FakeDisplay display, 257 DisplayModeWrapper[] wrappedModes) 258 throws InterruptedException { 259 // Update the display. 260 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length]; 261 for (int i = 0; i < wrappedModes.length; i++) { 262 modes[i] = wrappedModes[i].mode; 263 } 264 display.dynamicInfo.supportedDisplayModes = modes; 265 setUpDisplay(display); 266 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 267 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 268 assertTrue(mListener.traversalRequested); 269 assertThat(mListener.changedDisplays.size()).isGreaterThan(0); 270 271 // Verify the supported modes are updated accordingly. 272 DisplayDevice displayDevice = 273 mListener.changedDisplays.get(mListener.changedDisplays.size() - 1); 274 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 275 Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes; 276 assertThat(supportedModes.length).isEqualTo(modes.length); 277 278 for (int i = 0; i < wrappedModes.length; i++) { 279 assertModeIsSupported(supportedModes, modes[i], 280 wrappedModes[i].expectedAlternativeRefreshRates); 281 } 282 } 283 284 @Test testAfterDisplayChange_AlternativeRefreshRatesAreUpdated()285 public void testAfterDisplayChange_AlternativeRefreshRatesAreUpdated() throws Exception { 286 FakeDisplay display = new FakeDisplay(PORT_A); 287 setUpDisplay(display); 288 updateAvailableDisplays(); 289 mAdapter.registerLocked(); 290 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 291 292 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 293 new DisplayModeWrapper( 294 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{24f, 50f}), 295 new DisplayModeWrapper( 296 createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{24f, 60f}), 297 new DisplayModeWrapper( 298 createFakeDisplayMode(2, 1920, 1080, 24f, 0), new float[]{50f, 60f}), 299 new DisplayModeWrapper( 300 createFakeDisplayMode(3, 3840, 2160, 60f, 0), new float[]{24f, 50f}), 301 new DisplayModeWrapper( 302 createFakeDisplayMode(4, 3840, 2160, 50f, 0), new float[]{24f, 60f}), 303 new DisplayModeWrapper( 304 createFakeDisplayMode(5, 3840, 2160, 24f, 0), new float[]{50f, 60f}), 305 }); 306 307 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 308 new DisplayModeWrapper( 309 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{50f}), 310 new DisplayModeWrapper( 311 createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{60f}), 312 new DisplayModeWrapper( 313 createFakeDisplayMode(2, 1920, 1080, 24f, 1), new float[0]), 314 new DisplayModeWrapper( 315 createFakeDisplayMode(3, 3840, 2160, 60f, 2), new float[0]), 316 new DisplayModeWrapper( 317 createFakeDisplayMode(4, 3840, 2160, 50f, 3), new float[]{24f}), 318 new DisplayModeWrapper( 319 createFakeDisplayMode(5, 3840, 2160, 24f, 3), new float[]{50f}), 320 }); 321 322 testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { 323 new DisplayModeWrapper( 324 createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[0]), 325 new DisplayModeWrapper( 326 createFakeDisplayMode(1, 1920, 1080, 50f, 1), new float[0]), 327 new DisplayModeWrapper( 328 createFakeDisplayMode(2, 1920, 1080, 24f, 2), new float[0]), 329 new DisplayModeWrapper( 330 createFakeDisplayMode(3, 3840, 2160, 60f, 3), new float[0]), 331 new DisplayModeWrapper( 332 createFakeDisplayMode(4, 3840, 2160, 50f, 4), new float[0]), 333 new DisplayModeWrapper( 334 createFakeDisplayMode(5, 3840, 2160, 24f, 5), new float[0]), 335 }); 336 } 337 338 @Test testAfterDisplayChange_DisplayModesAreUpdated()339 public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception { 340 SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f); 341 SurfaceControl.DisplayMode[] modes = 342 new SurfaceControl.DisplayMode[]{displayMode}; 343 FakeDisplay display = new FakeDisplay(PORT_A, modes, 0); 344 setUpDisplay(display); 345 updateAvailableDisplays(); 346 mAdapter.registerLocked(); 347 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 348 349 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 350 assertThat(mListener.changedDisplays).isEmpty(); 351 352 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 353 0).getDisplayDeviceInfoLocked(); 354 355 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 356 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 357 358 Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 359 assertThat(defaultMode.matches(displayMode.width, displayMode.height, 360 displayMode.refreshRate)).isTrue(); 361 362 Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 363 assertThat(activeMode.matches(displayMode.width, displayMode.height, 364 displayMode.refreshRate)).isTrue(); 365 366 // Change the display 367 SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f); 368 modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo}; 369 display.dynamicInfo.supportedDisplayModes = modes; 370 display.dynamicInfo.activeDisplayModeId = 1; 371 setUpDisplay(display); 372 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 373 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 374 375 assertTrue(mListener.traversalRequested); 376 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 377 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 378 379 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 380 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 381 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 382 383 assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); 384 assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); 385 assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo); 386 387 activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 388 assertThat(activeMode.matches(addedDisplayInfo.width, addedDisplayInfo.height, 389 addedDisplayInfo.refreshRate)).isTrue(); 390 391 defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); 392 assertThat(defaultMode.matches(addedDisplayInfo.width, addedDisplayInfo.height, 393 addedDisplayInfo.refreshRate)).isTrue(); 394 } 395 396 @Test testAfterDisplayChange_ActiveModeIsUpdated()397 public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception { 398 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ 399 createFakeDisplayMode(0, 1920, 1080, 60f), 400 createFakeDisplayMode(1, 1920, 1080, 50f) 401 }; 402 FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0); 403 setUpDisplay(display); 404 updateAvailableDisplays(); 405 mAdapter.registerLocked(); 406 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 407 408 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 409 assertThat(mListener.changedDisplays).isEmpty(); 410 411 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 412 .getDisplayDeviceInfoLocked(); 413 414 Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 415 assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); 416 417 // Change the display 418 display.dynamicInfo.activeDisplayModeId = 1; 419 setUpDisplay(display); 420 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 421 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 422 423 assertTrue(mListener.traversalRequested); 424 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 425 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 426 427 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 428 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 429 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 430 431 activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); 432 assertThat(activeMode.matches(1920, 1080, 50f)).isTrue(); 433 } 434 435 @Test testAfterDisplayChange_HdrCapabilitiesAreUpdated()436 public void testAfterDisplayChange_HdrCapabilitiesAreUpdated() throws Exception { 437 FakeDisplay display = new FakeDisplay(PORT_A); 438 Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0], 439 1000, 1000, 0); 440 display.dynamicInfo.hdrCapabilities = initialHdrCapabilities; 441 setUpDisplay(display); 442 updateAvailableDisplays(); 443 mAdapter.registerLocked(); 444 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 445 446 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 447 assertThat(mListener.changedDisplays).isEmpty(); 448 449 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 450 0).getDisplayDeviceInfoLocked(); 451 452 assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(initialHdrCapabilities); 453 454 // Change the display 455 Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities( 456 new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0); 457 display.dynamicInfo.hdrCapabilities = changedHdrCapabilities; 458 setUpDisplay(display); 459 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 460 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 461 462 assertTrue(mListener.traversalRequested); 463 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 464 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 465 466 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 467 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 468 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 469 470 assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(changedHdrCapabilities); 471 } 472 473 @Test testAfterDisplayChange_AllmSupportIsUpdated()474 public void testAfterDisplayChange_AllmSupportIsUpdated() throws Exception { 475 FakeDisplay display = new FakeDisplay(PORT_A); 476 display.dynamicInfo.autoLowLatencyModeSupported = true; 477 setUpDisplay(display); 478 updateAvailableDisplays(); 479 mAdapter.registerLocked(); 480 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 481 482 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 483 assertThat(mListener.changedDisplays).isEmpty(); 484 485 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 486 .getDisplayDeviceInfoLocked(); 487 488 assertThat(displayDeviceInfo.allmSupported).isTrue(); 489 490 // Change the display 491 display.dynamicInfo.autoLowLatencyModeSupported = false; 492 setUpDisplay(display); 493 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 494 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 495 496 assertTrue(mListener.traversalRequested); 497 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 498 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 499 500 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 501 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 502 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 503 504 assertThat(displayDeviceInfo.allmSupported).isFalse(); 505 } 506 507 @Test testAfterDisplayChange_GameContentTypeSupportIsUpdated()508 public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception { 509 FakeDisplay display = new FakeDisplay(PORT_A); 510 display.dynamicInfo.gameContentTypeSupported = true; 511 setUpDisplay(display); 512 updateAvailableDisplays(); 513 mAdapter.registerLocked(); 514 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 515 516 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 517 assertThat(mListener.changedDisplays).isEmpty(); 518 519 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 520 .getDisplayDeviceInfoLocked(); 521 522 assertThat(displayDeviceInfo.gameContentTypeSupported).isTrue(); 523 524 // Change the display 525 display.dynamicInfo.gameContentTypeSupported = false; 526 setUpDisplay(display); 527 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 528 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 529 530 assertTrue(mListener.traversalRequested); 531 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 532 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 533 534 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 535 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 536 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 537 538 assertThat(displayDeviceInfo.gameContentTypeSupported).isFalse(); 539 } 540 541 @Test testAfterDisplayChange_ColorModesAreUpdated()542 public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception { 543 FakeDisplay display = new FakeDisplay(PORT_A); 544 final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709}; 545 display.dynamicInfo.supportedColorModes = initialColorModes; 546 setUpDisplay(display); 547 updateAvailableDisplays(); 548 mAdapter.registerLocked(); 549 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 550 551 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 552 assertThat(mListener.changedDisplays).isEmpty(); 553 554 DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) 555 .getDisplayDeviceInfoLocked(); 556 557 assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_BT709); 558 assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(initialColorModes); 559 560 // Change the display 561 final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT}; 562 display.dynamicInfo.supportedColorModes = changedColorModes; 563 setUpDisplay(display); 564 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 565 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 566 567 assertTrue(mListener.traversalRequested); 568 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 569 assertThat(mListener.changedDisplays.size()).isEqualTo(1); 570 571 DisplayDevice displayDevice = mListener.changedDisplays.get(0); 572 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 573 displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); 574 575 assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_DEFAULT); 576 assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes); 577 } 578 579 @Test testDisplayChange_withStaleDesiredDisplayModeSpecs()580 public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception { 581 SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ 582 createFakeDisplayMode(0, 1920, 1080, 60f), 583 createFakeDisplayMode(1, 1920, 1080, 50f) 584 }; 585 final int activeMode = 0; 586 FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode); 587 display.desiredDisplayModeSpecs.defaultMode = 1; 588 589 setUpDisplay(display); 590 updateAvailableDisplays(); 591 mAdapter.registerLocked(); 592 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 593 594 assertThat(mListener.addedDisplays.size()).isEqualTo(1); 595 DisplayDevice displayDevice = mListener.addedDisplays.get(0); 596 597 int baseModeId = Arrays.stream(displayDevice.getDisplayDeviceInfoLocked().supportedModes) 598 .filter(mode -> mode.getRefreshRate() == 60f) 599 .findFirst() 600 .get() 601 .getModeId(); 602 603 displayDevice.setDesiredDisplayModeSpecsLocked( 604 new DisplayModeDirector.DesiredDisplayModeSpecs( 605 /*baseModeId*/ baseModeId, 606 /*allowGroupSwitching*/ false, 607 new RefreshRateRange(60f, 60f), 608 new RefreshRateRange(60f, 60f) 609 )); 610 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 611 verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, 612 new SurfaceControl.DesiredDisplayModeSpecs( 613 /* baseModeId */ 0, 614 /* allowGroupSwitching */ false, 615 /* primaryRange */ 60f, 60f, 616 /* appRange */ 60f, 60f 617 )); 618 619 // Change the display 620 display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ 621 createFakeDisplayMode(2, 1920, 1080, 60f) 622 }; 623 display.dynamicInfo.activeDisplayModeId = 2; 624 // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't crash. 625 display.desiredDisplayModeSpecs.defaultMode = 1; 626 627 setUpDisplay(display); 628 mInjector.getTransmitter().sendHotplug(display, /* connected */ true); 629 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 630 631 assertTrue(mListener.traversalRequested); 632 633 displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); 634 635 baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId(); 636 637 // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device 638 displayDevice.setDesiredDisplayModeSpecsLocked( 639 new DisplayModeDirector.DesiredDisplayModeSpecs( 640 /*baseModeId*/ baseModeId, 641 /*allowGroupSwitching*/ false, 642 new RefreshRateRange(60f, 60f), 643 new RefreshRateRange(60f, 60f) 644 )); 645 646 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 647 648 // Verify that this will reapply the desired modes. 649 verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, 650 new SurfaceControl.DesiredDisplayModeSpecs( 651 /* baseModeId */ 2, 652 /* allowGroupSwitching */ false, 653 /* primaryRange */ 60f, 60f, 654 /* appRange */ 60f, 60f 655 )); 656 } 657 658 @Test testBacklightAdapter_withSurfaceControlSupport()659 public void testBacklightAdapter_withSurfaceControlSupport() { 660 final Binder displayToken = new Binder(); 661 662 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(true); 663 664 // Test as default display 665 BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, 666 mSurfaceControlProxy); 667 ba.setBacklight(0.514f, 100f, 0.614f, 500f); 668 verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f, 100f, 0.614f, 500f); 669 670 // Test as not default display 671 BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, 672 mSurfaceControlProxy); 673 ba2.setBacklight(0.323f, 101f, 0.723f, 601f); 674 verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f, 101f, 0.723f, 601f); 675 } 676 677 @Test testBacklightAdapter_withoutSourceControlSupport_defaultDisplay()678 public void testBacklightAdapter_withoutSourceControlSupport_defaultDisplay() { 679 final Binder displayToken = new Binder(); 680 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); 681 doReturn(mMockedBacklight).when(mMockedLightsManager) 682 .getLight(LightsManager.LIGHT_ID_BACKLIGHT); 683 684 BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, 685 mSurfaceControlProxy); 686 ba.setBacklight(1f, 1f, 0.123f, 1f); 687 verify(mMockedBacklight).setBrightness(0.123f); 688 } 689 690 @Test testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay()691 public void testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay() { 692 final Binder displayToken = new Binder(); 693 when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); 694 doReturn(mMockedBacklight).when(mMockedLightsManager) 695 .getLight(LightsManager.LIGHT_ID_BACKLIGHT); 696 697 BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, 698 mSurfaceControlProxy); 699 ba.setBacklight(0.456f, 1f, 1f, 1f); 700 701 // Adapter does not forward any brightness in this case. 702 verify(mMockedBacklight, never()).setBrightness(anyFloat()); 703 } 704 assertDisplayDpi(DisplayDeviceInfo info, int expectedPort, float expectedXdpi, float expectedYDpi, int expectedDensityDpi)705 private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort, 706 float expectedXdpi, 707 float expectedYDpi, 708 int expectedDensityDpi) { 709 final DisplayAddress.Physical physical = (DisplayAddress.Physical) info.address; 710 assertNotNull(physical); 711 assertEquals(expectedPort, physical.getPort()); 712 assertEquals(expectedXdpi, info.xDpi, 0.01); 713 assertEquals(expectedYDpi, info.yDpi, 0.01); 714 assertEquals(expectedDensityDpi, info.densityDpi); 715 } 716 getModeById(DisplayDeviceInfo displayDeviceInfo, int modeId)717 private Display.Mode getModeById(DisplayDeviceInfo displayDeviceInfo, int modeId) { 718 return Arrays.stream(displayDeviceInfo.supportedModes) 719 .filter(mode -> mode.getModeId() == modeId) 720 .findFirst() 721 .get(); 722 } 723 assertModeIsSupported(Display.Mode[] supportedModes, SurfaceControl.DisplayMode mode)724 private void assertModeIsSupported(Display.Mode[] supportedModes, 725 SurfaceControl.DisplayMode mode) { 726 assertThat(Arrays.stream(supportedModes).anyMatch( 727 x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue(); 728 } 729 assertModeIsSupported(Display.Mode[] supportedModes, SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates)730 private void assertModeIsSupported(Display.Mode[] supportedModes, 731 SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) { 732 float[] sortedAlternativeRates = 733 Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length); 734 Arrays.sort(sortedAlternativeRates); 735 736 String message = "Expected " + mode + " with alternativeRefreshRates = " 737 + Arrays.toString(alternativeRefreshRates) + " to be in list of supported modes = " 738 + Arrays.toString(supportedModes); 739 Truth.assertWithMessage(message) 740 .that(Arrays.stream(supportedModes) 741 .anyMatch(x -> x.matches(mode.width, mode.height, mode.refreshRate) 742 && Arrays.equals(x.getAlternativeRefreshRates(), sortedAlternativeRates))) 743 .isTrue(); 744 } 745 746 747 private static class FakeDisplay { 748 public final DisplayAddress.Physical address; 749 public final IBinder token = new Binder(); 750 public final SurfaceControl.StaticDisplayInfo info; 751 public SurfaceControl.DynamicDisplayInfo dynamicInfo = 752 new SurfaceControl.DynamicDisplayInfo(); 753 { 754 dynamicInfo.supportedColorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; 755 dynamicInfo.hdrCapabilities = new Display.HdrCapabilities(new int[0], 756 1000, 1000, 0); 757 } 758 759 public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs = 760 new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0, 761 /* allowGroupSwitching */ false, 762 /* primaryRefreshRateMin */ 60.f, 763 /* primaryRefreshRateMax */ 60.f, 764 /* appRefreshRateMin */ 60.f, 765 /* appRefreshRateMax */60.f); 766 FakeDisplay(int port)767 private FakeDisplay(int port) { 768 address = createDisplayAddress(port); 769 info = createFakeDisplayInfo(); 770 dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ 771 createFakeDisplayMode(0, 800, 600, 60f) 772 }; 773 dynamicInfo.activeDisplayModeId = 0; 774 } 775 FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode)776 private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) { 777 address = createDisplayAddress(port); 778 info = createFakeDisplayInfo(); 779 dynamicInfo.supportedDisplayModes = modes; 780 dynamicInfo.activeDisplayModeId = activeMode; 781 } 782 } 783 setUpDisplay(FakeDisplay display)784 private void setUpDisplay(FakeDisplay display) { 785 mAddresses.add(display.address); 786 when(mSurfaceControlProxy.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())) 787 .thenReturn(display.token); 788 when(mSurfaceControlProxy.getStaticDisplayInfo(display.token)) 789 .thenReturn(display.info); 790 when(mSurfaceControlProxy.getDynamicDisplayInfo(display.token)) 791 .thenReturn(display.dynamicInfo); 792 when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)) 793 .thenReturn(display.desiredDisplayModeSpecs); 794 } 795 updateAvailableDisplays()796 private void updateAvailableDisplays() { 797 long[] ids = new long[mAddresses.size()]; 798 int i = 0; 799 for (DisplayAddress.Physical address : mAddresses) { 800 ids[i] = address.getPhysicalDisplayId(); 801 i++; 802 } 803 when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids); 804 } 805 createDisplayAddress(int port)806 private static DisplayAddress.Physical createDisplayAddress(int port) { 807 return DisplayAddress.fromPortAndModel(port, DISPLAY_MODEL); 808 } 809 createFakeDisplayInfo()810 private static SurfaceControl.StaticDisplayInfo createFakeDisplayInfo() { 811 final SurfaceControl.StaticDisplayInfo info = new SurfaceControl.StaticDisplayInfo(); 812 info.density = 100; 813 return info; 814 } 815 createFakeDisplayMode(int id, int width, int height, float refreshRate)816 private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, 817 float refreshRate) { 818 return createFakeDisplayMode(id, width, height, refreshRate, /* group */ 0); 819 } 820 createFakeDisplayMode(int id, int width, int height, float refreshRate, int group)821 private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, 822 float refreshRate, int group) { 823 final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode(); 824 mode.id = id; 825 mode.width = width; 826 mode.height = height; 827 mode.refreshRate = refreshRate; 828 mode.xDpi = 100; 829 mode.yDpi = 100; 830 mode.group = group; 831 return mode; 832 } 833 waitForHandlerToComplete(Handler handler, long waitTimeMs)834 private static void waitForHandlerToComplete(Handler handler, long waitTimeMs) 835 throws InterruptedException { 836 final Object lock = new Object(); 837 synchronized (lock) { 838 handler.post(() -> { 839 synchronized (lock) { 840 lock.notify(); 841 } 842 }); 843 lock.wait(waitTimeMs); 844 } 845 } 846 847 private class HotplugTransmitter { 848 private final Handler mHandler; 849 private final LocalDisplayAdapter.DisplayEventListener mListener; 850 HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener)851 HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener) { 852 mHandler = new Handler(looper); 853 mListener = listener; 854 } 855 sendHotplug(FakeDisplay display, boolean connected)856 public void sendHotplug(FakeDisplay display, boolean connected) 857 throws InterruptedException { 858 mHandler.post(() -> mListener.onHotplug(/* timestampNanos = */ 0, 859 display.address.getPhysicalDisplayId(), connected)); 860 waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); 861 } 862 } 863 864 private class Injector extends LocalDisplayAdapter.Injector { 865 private HotplugTransmitter mTransmitter; 866 @Override setDisplayEventListenerLocked(Looper looper, LocalDisplayAdapter.DisplayEventListener listener)867 public void setDisplayEventListenerLocked(Looper looper, 868 LocalDisplayAdapter.DisplayEventListener listener) { 869 mTransmitter = new HotplugTransmitter(looper, listener); 870 } 871 getTransmitter()872 public HotplugTransmitter getTransmitter() { 873 return mTransmitter; 874 } 875 876 @Override getSurfaceControlProxy()877 public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { 878 return mSurfaceControlProxy; 879 } 880 } 881 882 private class TestListener implements DisplayAdapter.Listener { 883 public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>(); 884 public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>(); 885 public boolean traversalRequested = false; 886 887 @Override onDisplayDeviceEvent(DisplayDevice device, int event)888 public void onDisplayDeviceEvent(DisplayDevice device, int event) { 889 if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED) { 890 addedDisplays.add(device); 891 } else if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED) { 892 changedDisplays.add(device); 893 } 894 } 895 896 @Override onTraversalRequested()897 public void onTraversalRequested() { 898 traversalRequested = true; 899 } 900 } 901 createFloatTypedArray(float[] vals)902 private TypedArray createFloatTypedArray(float[] vals) { 903 TypedArray mockArray = mock(TypedArray.class); 904 when(mockArray.length()).thenAnswer(invocation -> { 905 return vals.length; 906 }); 907 when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { 908 final float def = (float) invocation.getArguments()[1]; 909 if (vals == null) { 910 return def; 911 } 912 int idx = (int) invocation.getArguments()[0]; 913 if (idx >= 0 && idx < vals.length) { 914 return vals[idx]; 915 } else { 916 return def; 917 } 918 }); 919 return mockArray; 920 } 921 } 922