1 /* 2 * Copyright (C) 2021 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.compatibility.common.util; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.content.res.Resources; 22 import android.hardware.display.DisplayManager; 23 import android.hardware.hdmi.HdmiControlManager; 24 import android.view.Display; 25 26 import androidx.test.InstrumentationRegistry; 27 28 import java.util.concurrent.CountDownLatch; 29 import java.util.concurrent.TimeUnit; 30 31 public class DisplayUtil { 32 33 private static final float REFRESH_RATE_TOLERANCE = 0.01f; 34 35 /** 36 * Returns if a physical display is connected to DUT. The method may return a false positive, 37 * but no false negative. 38 */ isDisplayConnected(Context context)39 public static boolean isDisplayConnected(Context context) throws Exception { 40 // DisplayManager will return a display even if there is no connected display. 41 // For that reason we use HdmiControlManager to check if there's something connected 42 // to the HDMI port. 43 HdmiControlManager cecManager = context.getSystemService(HdmiControlManager.class); 44 45 if (cecManager == null) { 46 // CEC is not available. Can't do anything more, so assume that there is a display. 47 return true; 48 } 49 50 if (cecManager.getPlaybackClient() == null) { 51 // The device is not HDMI playback device (e.g. set-top box), so we assume it has 52 // a built-in display. 53 return true; 54 } 55 56 CountDownLatch notifyLatch = new CountDownLatch(1); 57 cecManager.addHotplugEventListener(event -> { 58 // TODO(b/189837682): Check if the port is HDMI out 59 if (event.isConnected()) { 60 notifyLatch.countDown(); 61 } 62 }); 63 64 return notifyLatch.await(3, TimeUnit.SECONDS); 65 } 66 isModeSwitchSeamless(Display.Mode from, Display.Mode to)67 public static boolean isModeSwitchSeamless(Display.Mode from, Display.Mode to) { 68 if (from.getModeId() == to.getModeId()) { 69 return true; 70 } 71 72 if (from.getPhysicalHeight() != to.getPhysicalHeight() 73 || from.getPhysicalWidth() != to.getPhysicalWidth()) { 74 return false; 75 } 76 77 for (float alternativeRefreshRate : from.getAlternativeRefreshRates()) { 78 if (Math.abs(alternativeRefreshRate - to.getRefreshRate()) < REFRESH_RATE_TOLERANCE) { 79 return true; 80 } 81 } 82 83 return false; 84 } 85 getRefreshRateSwitchingType(DisplayManager displayManager)86 public static int getRefreshRateSwitchingType(DisplayManager displayManager) { 87 return toSwitchingType(displayManager.getMatchContentFrameRateUserPreference()); 88 } 89 toSwitchingType(int matchContentFrameRateUserPreference)90 private static int toSwitchingType(int matchContentFrameRateUserPreference) { 91 switch (matchContentFrameRateUserPreference) { 92 case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER: 93 return DisplayManager.SWITCHING_TYPE_NONE; 94 case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY: 95 return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; 96 case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS: 97 return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; 98 default: 99 return -1; 100 } 101 } 102 hasDeviceFeature(final String requiredFeature)103 private static boolean hasDeviceFeature(final String requiredFeature) { 104 return InstrumentationRegistry.getContext() 105 .getPackageManager() 106 .hasSystemFeature(requiredFeature); 107 } 108 isSystemConfigSupported(final String configName)109 private static boolean isSystemConfigSupported(final String configName) { 110 try { 111 return InstrumentationRegistry.getContext().getResources().getBoolean( 112 Resources.getSystem().getIdentifier( 113 configName, "bool", "android")); 114 } catch (Resources.NotFoundException e) { 115 // Assume this device supports the config. 116 return true; 117 } 118 } 119 120 /** 121 * Gets whether the device supports auto rotation. In general such a 122 * device has an accelerometer, has the portrait and landscape features, and 123 * has the config_supportAutoRotation resource. 124 */ supportsAutoRotation()125 public static boolean supportsAutoRotation() { 126 return hasDeviceFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER) 127 && hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) 128 && hasDeviceFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE) 129 && isSystemConfigSupported("config_supportAutoRotation"); 130 } 131 } 132