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