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