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