• Home
  • History
  • Annotate
  • 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      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