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