• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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