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 android.mediapc.cts.common; 18 19 import static android.util.DisplayMetrics.DENSITY_400; 20 21 import static org.junit.Assume.assumeTrue; 22 23 import android.app.ActivityManager; 24 import android.content.Context; 25 import android.content.pm.PackageManager; 26 import android.hardware.display.DisplayManager; 27 import android.media.MediaCodec; 28 import android.media.MediaCodecInfo; 29 import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint; 30 import android.media.MediaFormat; 31 import android.os.Build; 32 import android.os.SystemProperties; 33 import android.util.DisplayMetrics; 34 import android.util.Log; 35 import android.view.Display; 36 37 import androidx.test.platform.app.InstrumentationRegistry; 38 39 import com.android.compatibility.common.util.ApiLevelUtil; 40 41 import java.io.IOException; 42 import java.util.Arrays; 43 import java.util.Comparator; 44 import java.util.List; 45 import java.util.stream.Stream; 46 47 /** 48 * Test utilities. 49 */ 50 public class Utils { 51 private static final int sPc; 52 53 private static final String TAG = "PerformanceClassTestUtils"; 54 private static final String MEDIA_PERF_CLASS_KEY = "media-performance-class"; 55 56 public static final int DISPLAY_DPI; 57 public static final int MIN_DISPLAY_CANDIDATE_DPI = DENSITY_400; 58 public static final int DISPLAY_LONG_PIXELS; 59 public static final int MIN_DISPLAY_LONG_CANDIDATE_PIXELS = 1920; 60 public static final int DISPLAY_SHORT_PIXELS; 61 public static final int MIN_DISPLAY_SHORT_CANDIDATE_PIXELS = 1080; 62 public static final boolean IS_HDR; 63 public static final float HDR_DISPLAY_AVERAGE_LUMINANCE; 64 65 public static final long TOTAL_MEMORY_MB; 66 // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB 67 // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device. 68 public static final long MIN_MEMORY_PERF_CLASS_CANDIDATE_MB = 5 * 1024; 69 // Android T Media performance requires 8 GB min RAM, so setting lower as above 70 public static final long MIN_MEMORY_PERF_CLASS_T_MB = 6800; 71 72 private static final boolean MEETS_AVC_CODEC_PRECONDITIONS; 73 static { 74 // with a default-media-performance-class that can be configured through a command line 75 // argument. 76 android.os.Bundle args; 77 try { 78 args = InstrumentationRegistry.getArguments(); 79 } catch (Exception e) { 80 args = null; 81 } 82 if (args != null) { 83 String mediaPerfClassArg = args.getString(MEDIA_PERF_CLASS_KEY); 84 if (mediaPerfClassArg != null) { Log.d(TAG, "Running the tests with performance class set to " + mediaPerfClassArg)85 Log.d(TAG, "Running the tests with performance class set to " + mediaPerfClassArg); 86 sPc = Integer.parseInt(mediaPerfClassArg); 87 } else { 88 sPc = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S) 89 ? Build.VERSION.MEDIA_PERFORMANCE_CLASS 90 : SystemProperties.getInt("ro.odm.build.media_performance_class", 0); 91 } Log.d(TAG, "performance class is " + sPc)92 Log.d(TAG, "performance class is " + sPc); 93 } else { 94 sPc = 0; 95 } 96 97 Context context; 98 try { 99 context = InstrumentationRegistry.getInstrumentation().getContext(); 100 } catch (Exception e) { 101 context = null; 102 } 103 // When used from ItsService, context will be null 104 if (context != null) { 105 DisplayManager displayManager = context.getSystemService(DisplayManager.class); 106 Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 107 Display.Mode maxResolutionDisplayMode = 108 Arrays.stream(displayManager.getDisplays()) 109 .map(Display::getSupportedModes) 110 .flatMap(Stream::of) 111 .max(Comparator.comparing(Display.Mode::getPhysicalHeight)) 112 .orElseThrow( 113 () -> new RuntimeException("Failed to determine max height")); 114 int maxWidthPixels = maxResolutionDisplayMode.getPhysicalWidth(); 115 int maxHeightPixels = maxResolutionDisplayMode.getPhysicalHeight(); 116 DISPLAY_LONG_PIXELS = Math.max(maxWidthPixels, maxHeightPixels); 117 DISPLAY_SHORT_PIXELS = Math.min(maxWidthPixels, maxHeightPixels); 118 119 int widthPixels = defaultDisplay.getMode().getPhysicalWidth(); 120 int heightPixels = defaultDisplay.getMode().getPhysicalHeight(); 121 122 DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 123 final double widthInch = (double) widthPixels / (double) metrics.xdpi; 124 final double heightInch = (double) heightPixels / (double) metrics.ydpi; 125 final double diagonalInch = Math.sqrt(widthInch * widthInch + heightInch * heightInch); 126 final double maxDiagonalPixels = 127 Math.sqrt(maxWidthPixels * maxWidthPixels + maxHeightPixels * maxHeightPixels); 128 // Use max of computed dpi and advertised dpi as these values differ in some devices. 129 DISPLAY_DPI = Math.max((int) (maxDiagonalPixels / diagonalInch), 130 context.getResources().getConfiguration().densityDpi); 131 132 IS_HDR = defaultDisplay.isHdr(); 133 HDR_DISPLAY_AVERAGE_LUMINANCE = 134 defaultDisplay.getHdrCapabilities().getDesiredMaxAverageLuminance(); 135 136 ActivityManager activityManager = context.getSystemService(ActivityManager.class); 137 ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); 138 activityManager.getMemoryInfo(memoryInfo); 139 TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024; 140 } else { 141 DISPLAY_DPI = 0; 142 DISPLAY_LONG_PIXELS = 0; 143 DISPLAY_SHORT_PIXELS = 0; 144 TOTAL_MEMORY_MB = 0; 145 IS_HDR = false; 146 HDR_DISPLAY_AVERAGE_LUMINANCE = 0; 147 } 148 MEETS_AVC_CODEC_PRECONDITIONS = meetsAvcCodecPreconditions(); 149 } 150 151 /** 152 * First defined media performance class. 153 */ 154 private static final int FIRST_PERFORMANCE_CLASS = Build.VERSION_CODES.R; 155 isRPerfClass()156 public static boolean isRPerfClass() { 157 return sPc == Build.VERSION_CODES.R; 158 } 159 isSPerfClass()160 public static boolean isSPerfClass() { 161 return sPc == Build.VERSION_CODES.S; 162 } 163 isTPerfClass()164 public static boolean isTPerfClass() { 165 return sPc == Build.VERSION_CODES.TIRAMISU; 166 } 167 isBeforeTPerfClass()168 public static boolean isBeforeTPerfClass() { 169 return sPc < Build.VERSION_CODES.TIRAMISU; 170 } 171 isUPerfClass()172 public static boolean isUPerfClass() { 173 return sPc == Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 174 } 175 isVPerfClass()176 public static boolean isVPerfClass() { 177 return sPc == Build.VERSION_CODES.VANILLA_ICE_CREAM; 178 } 179 180 /** 181 * Latest defined media performance class. 182 */ 183 private static final int LAST_PERFORMANCE_CLASS = Build.VERSION_CODES.VANILLA_ICE_CREAM; 184 isHandheld()185 public static boolean isHandheld() { 186 // handheld nature is not exposed to package manager, for now 187 // we check for touchscreen and NOT watch and NOT tv 188 PackageManager pm = 189 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 190 return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN) 191 && !pm.hasSystemFeature(pm.FEATURE_WATCH) 192 && !pm.hasSystemFeature(pm.FEATURE_TELEVISION) 193 && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE); 194 } 195 meetsAvcCodecPreconditions(boolean isEncoder)196 private static boolean meetsAvcCodecPreconditions(boolean isEncoder) { 197 // Latency tests need the following instances of codecs at 30 fps 198 // 1920x1080 encoder in MediaRecorder for load conditions 199 // 1920x1080 decoder and 1920x1080 encoder for load conditions 200 // 1920x1080 encoder for initialization test 201 // Since there is no way to know if encoder and decoder are supported concurrently at their 202 // maximum load, we will test the above combined requirements are met for both encoder and 203 // decoder (so a minimum of 4 instances required for both encoder and decoder) 204 int minInstancesRequired = 4; 205 int width = 1920; 206 int height = 1080; 207 double fps = 30 /* encoder for media recorder */ 208 + 30 /* 1080p decoder for transcoder */ 209 + 30 /* 1080p encoder for transcoder */ 210 + 30 /* 1080p encoder for latency test */; 211 212 String avcMediaType = MediaFormat.MIMETYPE_VIDEO_AVC; 213 PerformancePoint pp1080p = new PerformancePoint(width, height, (int) fps); 214 MediaCodec codec; 215 try { 216 codec = isEncoder ? MediaCodec.createEncoderByType(avcMediaType) : 217 MediaCodec.createDecoderByType(avcMediaType); 218 } catch (IOException e) { 219 Log.d(TAG, "Unable to create codec " + e); 220 return false; 221 } 222 MediaCodecInfo info = codec.getCodecInfo(); 223 MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(avcMediaType); 224 List<PerformancePoint> pps = 225 caps.getVideoCapabilities().getSupportedPerformancePoints(); 226 if (pps == null || pps.size() == 0) { 227 Log.w(TAG, info.getName() + " doesn't advertise performance points. Assuming codec " 228 + "meets the requirements"); 229 codec.release(); 230 return true; 231 } 232 boolean supportsRequiredRate = false; 233 for (PerformancePoint pp : pps) { 234 if (pp.covers(pp1080p)) { 235 supportsRequiredRate = true; 236 } 237 } 238 239 boolean supportsRequiredSize = caps.getVideoCapabilities().isSizeSupported(width, height); 240 boolean supportsRequiredInstances = caps.getMaxSupportedInstances() >= minInstancesRequired; 241 codec.release(); 242 Log.d(TAG, info.getName() + " supports required FPS : " + supportsRequiredRate 243 + ", supports required size : " + supportsRequiredSize 244 + ", supports required instances : " + supportsRequiredInstances); 245 return supportsRequiredRate && supportsRequiredSize && supportsRequiredInstances; 246 } 247 meetsAvcCodecPreconditions()248 private static boolean meetsAvcCodecPreconditions() { 249 return meetsAvcCodecPreconditions(/* isEncoder */ true) 250 && meetsAvcCodecPreconditions(/* isEncoder */ false); 251 } 252 meetsMemoryPrecondition()253 private static boolean meetsMemoryPrecondition() { 254 if (isBeforeTPerfClass()) { 255 return TOTAL_MEMORY_MB >= MIN_MEMORY_PERF_CLASS_CANDIDATE_MB; 256 } else { 257 return TOTAL_MEMORY_MB >= MIN_MEMORY_PERF_CLASS_T_MB; 258 } 259 } 260 getPerfClass()261 public static int getPerfClass() { 262 return sPc; 263 } 264 isPerfClass()265 public static boolean isPerfClass() { 266 return sPc >= FIRST_PERFORMANCE_CLASS && 267 sPc <= LAST_PERFORMANCE_CLASS; 268 } 269 meetsPerformanceClassPreconditions()270 public static boolean meetsPerformanceClassPreconditions() { 271 if (isPerfClass()) { 272 return true; 273 } 274 275 // If device doesn't advertise performance class, check if this can be ruled out as a 276 // candidate for performance class tests. 277 if (!isHandheld() 278 || !meetsMemoryPrecondition() 279 || DISPLAY_DPI < MIN_DISPLAY_CANDIDATE_DPI 280 || DISPLAY_LONG_PIXELS < MIN_DISPLAY_LONG_CANDIDATE_PIXELS 281 || DISPLAY_SHORT_PIXELS < MIN_DISPLAY_SHORT_CANDIDATE_PIXELS 282 || !MEETS_AVC_CODEC_PRECONDITIONS) { 283 return false; 284 } 285 return true; 286 } 287 assumeDeviceMeetsPerformanceClassPreconditions()288 public static void assumeDeviceMeetsPerformanceClassPreconditions() { 289 assumeTrue( 290 "Test skipped because the device does not meet the hardware requirements for " 291 + "performance class.", 292 meetsPerformanceClassPreconditions()); 293 } 294 } 295