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