1 /*
2  * Copyright (C) 2012 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.media.misc.cts;
18 
19 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
20 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback;
21 
22 import android.content.pm.PackageManager;
23 import android.media.MediaCodec;
24 import android.media.MediaCodecInfo;
25 import android.media.MediaCodecInfo.AudioCapabilities;
26 import android.media.MediaCodecInfo.CodecCapabilities;
27 import android.media.MediaCodecInfo.EncoderCapabilities;
28 import android.media.MediaCodecInfo.VideoCapabilities;
29 import android.media.MediaCodecList;
30 import android.media.MediaFormat;
31 import android.os.Build;
32 import android.platform.test.annotations.Presubmit;
33 import android.platform.test.annotations.RequiresDevice;
34 import android.test.AndroidTestCase;
35 import android.util.Log;
36 import android.util.Pair;
37 import android.util.Range;
38 import android.util.Size;
39 
40 import androidx.test.filters.SmallTest;
41 
42 import com.android.compatibility.common.util.ApiLevelUtil;
43 import com.android.compatibility.common.util.MediaUtils;
44 import com.android.compatibility.common.util.PropertyUtil;
45 
46 import java.io.File;
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Set;
53 
54 @Presubmit
55 @SmallTest
56 @RequiresDevice
57 public class MediaCodecListTest extends AndroidTestCase {
58 
59     private static final String TAG = "MediaCodecListTest";
60     private static final String MEDIA_CODEC_XML_FILE = "/etc/media_codecs.xml";
61     private static final String VENDOR_MEDIA_CODEC_XML_FILE = "/vendor/etc/media_codecs.xml";
62     private static final String ODM_MEDIA_CODEC_XML_FILE = "/odm/etc/media_codecs.xml";
63     private final MediaCodecList mRegularCodecs =
64             new MediaCodecList(MediaCodecList.REGULAR_CODECS);
65     private final MediaCodecList mAllCodecs =
66             new MediaCodecList(MediaCodecList.ALL_CODECS);
67     private final MediaCodecInfo[] mRegularInfos =
68             mRegularCodecs.getCodecInfos();
69     private final MediaCodecInfo[] mAllInfos =
70             mAllCodecs.getCodecInfos();
71 
72     private static boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
73 
74     class CodecType {
CodecType(String type, boolean isEncoder, MediaFormat sampleFormat)75         CodecType(String type, boolean isEncoder, MediaFormat sampleFormat) {
76             mMimeTypeName = type;
77             mIsEncoder = isEncoder;
78             mSampleFormat = sampleFormat;
79         }
80 
equals(CodecType codecType)81         boolean equals(CodecType codecType) {
82             return (mMimeTypeName.compareTo(codecType.mMimeTypeName) == 0) &&
83                     mIsEncoder == codecType.mIsEncoder;
84         }
85 
canBeFound()86         boolean canBeFound() {
87             return codecCanBeFound(mIsEncoder, mSampleFormat);
88         }
89 
90         @Override
toString()91         public String toString() {
92             return mMimeTypeName + (mIsEncoder ? " encoder" : " decoder") + " for " + mSampleFormat;
93         }
94 
95         private String mMimeTypeName;
96         private boolean mIsEncoder;
97         private MediaFormat mSampleFormat;
98     };
99 
100     class AudioCodec extends CodecType {
AudioCodec(String mime, boolean isEncoder, int sampleRate)101         AudioCodec(String mime, boolean isEncoder, int sampleRate) {
102             super(mime, isEncoder, MediaFormat.createAudioFormat(
103                     mime, sampleRate, 1 /* channelCount */));
104         }
105     }
106 
107     class VideoCodec extends CodecType {
VideoCodec(String mime, boolean isEncoder)108         VideoCodec(String mime, boolean isEncoder) {
109             // implicit assumption that QVGA video is always valid
110             super(mime, isEncoder, MediaFormat.createVideoFormat(
111                     mime, 176 /* width */, 144 /* height */));
112         }
113     }
114 
hasExpandedCodecInfo()115     public static boolean hasExpandedCodecInfo() {
116         return PropertyUtil.isVendorApiLevelNewerThan(29);
117     }
118 
testMediaCodecXmlFileExist()119     public static void testMediaCodecXmlFileExist() {
120         File file = new File(MEDIA_CODEC_XML_FILE);
121         File vendorFile = new File(VENDOR_MEDIA_CODEC_XML_FILE);
122         File odmFile = new File(ODM_MEDIA_CODEC_XML_FILE);
123         assertTrue("media_codecs.xml does not exist in /odm/etc, /vendor/etc or /etc.",
124                 file.exists() || vendorFile.exists() || odmFile.exists());
125     }
126 
getLegacyInfos()127     private MediaCodecInfo[] getLegacyInfos() {
128         Log.d(TAG, "getLegacyInfos");
129 
130         int codecCount = MediaCodecList.getCodecCount();
131         MediaCodecInfo[] res = new MediaCodecInfo[codecCount];
132 
133         for (int i = 0; i < codecCount; ++i) {
134             res[i] = MediaCodecList.getCodecInfoAt(i);
135         }
136         return res;
137     }
138 
assertEqualsOrSuperset(Set big, Set tiny, boolean superset)139     public void assertEqualsOrSuperset(Set big, Set tiny, boolean superset) {
140         if (!superset) {
141             assertEquals(big, tiny);
142         } else {
143             assertTrue(big.containsAll(tiny));
144         }
145     }
146 
asSet(T[] array)147     private static <T> Set<T> asSet(T[] array) {
148         Set<T> s = new HashSet<T>();
149         for (T el : array) {
150             s.add(el);
151         }
152         return s;
153     }
154 
asSet(int[] array)155     private static Set<Integer> asSet(int[] array) {
156         Set<Integer> s = new HashSet<Integer>();
157         for (int el : array) {
158             s.add(el);
159         }
160         return s;
161     }
162 
assertEqualsOrSuperset( CodecCapabilities big, CodecCapabilities tiny, boolean superset)163     public void assertEqualsOrSuperset(
164             CodecCapabilities big, CodecCapabilities tiny, boolean superset) {
165         // ordering of enumerations may differ
166         assertEqualsOrSuperset(asSet(big.colorFormats), asSet(tiny.colorFormats), superset);
167         assertEqualsOrSuperset(asSet(big.profileLevels), asSet(tiny.profileLevels), superset);
168         AudioCapabilities bigAudCaps = big.getAudioCapabilities();
169         VideoCapabilities bigVidCaps = big.getVideoCapabilities();
170         EncoderCapabilities bigEncCaps = big.getEncoderCapabilities();
171         AudioCapabilities tinyAudCaps = tiny.getAudioCapabilities();
172         VideoCapabilities tinyVidCaps = tiny.getVideoCapabilities();
173         EncoderCapabilities tinyEncCaps = tiny.getEncoderCapabilities();
174         assertEquals(bigAudCaps != null, tinyAudCaps != null);
175         assertEquals(bigAudCaps != null, tinyAudCaps != null);
176         assertEquals(bigAudCaps != null, tinyAudCaps != null);
177     }
178 
assertEqualsOrSuperset( MediaCodecInfo big, MediaCodecInfo tiny, boolean superset)179     public void assertEqualsOrSuperset(
180             MediaCodecInfo big, MediaCodecInfo tiny, boolean superset) {
181         assertEquals(big.getName(), tiny.getName());
182         assertEquals(big.isEncoder(), tiny.isEncoder());
183         assertEqualsOrSuperset(
184                 asSet(big.getSupportedTypes()), asSet(tiny.getSupportedTypes()), superset);
185         for (String type : big.getSupportedTypes()) {
186             assertEqualsOrSuperset(
187                     big.getCapabilitiesForType(type),
188                     tiny.getCapabilitiesForType(type),
189                     superset);
190         }
191     }
192 
assertSuperset(MediaCodecInfo big, MediaCodecInfo tiny)193     public void assertSuperset(MediaCodecInfo big, MediaCodecInfo tiny) {
194         assertEqualsOrSuperset(big, tiny, true /* superset */);
195     }
196 
assertEquals(MediaCodecInfo big, MediaCodecInfo tiny)197     public void assertEquals(MediaCodecInfo big, MediaCodecInfo tiny) {
198         assertEqualsOrSuperset(big, tiny, false /* superset */);
199     }
200 
201     // Each component advertised by MediaCodecList should at least be
202     // instantiable.
testComponentInstantiation(MediaCodecInfo[] infos)203     private void testComponentInstantiation(MediaCodecInfo[] infos) throws IOException {
204         for (MediaCodecInfo info : infos) {
205             Log.d(TAG, "codec: " + info.getName());
206             Log.d(TAG, "  isEncoder = " + info.isEncoder());
207 
208             MediaCodec codec = MediaCodec.createByCodecName(info.getName());
209 
210             assertEquals(codec.getName(), info.getName());
211             assertEquals(codec.getCanonicalName(), info.getCanonicalName());
212             assertEquals(codec.getCodecInfo(), info);
213 
214             codec.release();
215             codec = null;
216         }
217     }
218 
testRegularComponentInstantiation()219     public void testRegularComponentInstantiation() throws IOException {
220         Log.d(TAG, "testRegularComponentInstantiation");
221         testComponentInstantiation(mRegularInfos);
222     }
223 
testAllComponentInstantiation()224     public void testAllComponentInstantiation() throws IOException {
225         Log.d(TAG, "testAllComponentInstantiation");
226         testComponentInstantiation(mAllInfos);
227     }
228 
testLegacyComponentInstantiation()229     public void testLegacyComponentInstantiation() throws IOException {
230         Log.d(TAG, "testLegacyComponentInstantiation");
231         testComponentInstantiation(getLegacyInfos());
232     }
233 
234     // For each type advertised by any of the components we should be able
235     // to get capabilities.
testGetCapabilities(MediaCodecInfo[] infos)236     private void testGetCapabilities(MediaCodecInfo[] infos) {
237         for (MediaCodecInfo info : infos) {
238             Log.d(TAG, "codec: " + info.getName());
239             Log.d(TAG, "  isEncoder = " + info.isEncoder());
240 
241             String[] types = info.getSupportedTypes();
242             for (int j = 0; j < types.length; ++j) {
243                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
244                 CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
245             }
246         }
247     }
248 
testGetRegularCapabilities()249     public void testGetRegularCapabilities() {
250         Log.d(TAG, "testGetRegularCapabilities");
251         testGetCapabilities(mRegularInfos);
252     }
253 
testGetAllCapabilities()254     public void testGetAllCapabilities() {
255         Log.d(TAG, "testGetAllCapabilities");
256         testGetCapabilities(mAllInfos);
257     }
258 
testGetLegacyCapabilities()259     public void testGetLegacyCapabilities() {
260         Log.d(TAG, "testGetLegacyCapabilities");
261         testGetCapabilities(getLegacyInfos());
262     }
263 
testLegacyMediaCodecListIsSameAsRegular()264     public void testLegacyMediaCodecListIsSameAsRegular() {
265         // regular codecs should be equivalent to legacy codecs, including
266         // codec ordering
267         MediaCodecInfo[] legacyInfos = getLegacyInfos();
268         assertEquals(legacyInfos.length, mRegularInfos.length);
269         for (int i = 0; i < legacyInfos.length; ++i) {
270             assertEquals(legacyInfos[i], mRegularInfos[i]);
271         }
272     }
273 
testRegularMediaCodecListIsASubsetOfAll()274     public void testRegularMediaCodecListIsASubsetOfAll() {
275         Log.d(TAG, "testRegularMediaCodecListIsASubsetOfAll");
276         // regular codecs should be a subsequence of all codecs, including
277         // codec ordering
278         int ix = 0;
279         for (MediaCodecInfo info : mAllInfos) {
280             if (ix == mRegularInfos.length) {
281                 break;
282             }
283             if (!mRegularInfos[ix].getName().equals(info.getName())) {
284                 Log.d(TAG, "skipping non-regular codec " + info.getName());
285                 continue;
286             }
287             Log.d(TAG, "checking codec " + info.getName());
288             assertSuperset(info, mRegularInfos[ix]);
289             ++ix;
290         }
291         assertEquals(
292                 "some regular codecs are not listed in all codecs", ix, mRegularInfos.length);
293     }
294 
testRequiredMediaCodecList()295     public void testRequiredMediaCodecList() {
296         List<CodecType> requiredList = getRequiredCodecTypes();
297         List<CodecType> supportedList = getSupportedCodecTypes();
298         assertTrue(areRequiredCodecTypesSupported(requiredList, supportedList));
299         for (CodecType type : requiredList) {
300             assertTrue("cannot find " + type, type.canBeFound());
301         }
302     }
303 
hasCamera()304     private boolean hasCamera() {
305         PackageManager pm = getContext().getPackageManager();
306         return pm.hasSystemFeature(pm.FEATURE_CAMERA_FRONT) ||
307                 pm.hasSystemFeature(pm.FEATURE_CAMERA);
308     }
309 
hasMicrophone()310     private boolean hasMicrophone() {
311         PackageManager pm = getContext().getPackageManager();
312         return pm.hasSystemFeature(pm.FEATURE_MICROPHONE);
313     }
314 
isWatch()315     private boolean isWatch() {
316         PackageManager pm = getContext().getPackageManager();
317         return pm.hasSystemFeature(pm.FEATURE_WATCH);
318     }
319 
isHandheld()320     private boolean isHandheld() {
321         // handheld nature is not exposed to package manager, for now
322         // we check for touchscreen and NOT watch and NOT tv
323         PackageManager pm = getContext().getPackageManager();
324         return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
325                 && !pm.hasSystemFeature(pm.FEATURE_WATCH)
326                 && !pm.hasSystemFeature(pm.FEATURE_TELEVISION)
327                 && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
328     }
329 
isAutomotive()330     private boolean isAutomotive() {
331         PackageManager pm = getContext().getPackageManager();
332         return pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
333     }
334 
isPC()335     private boolean isPC() {
336         PackageManager pm = getContext().getPackageManager();
337         return pm.hasSystemFeature(pm.FEATURE_PC);
338     }
339 
340     // Find whether the given codec can be found using MediaCodecList.find methods.
codecCanBeFound(boolean isEncoder, MediaFormat format)341     private boolean codecCanBeFound(boolean isEncoder, MediaFormat format) {
342         String codecName = isEncoder
343                 ? mRegularCodecs.findEncoderForFormat(format)
344                 : mRegularCodecs.findDecoderForFormat(format);
345         return codecName != null;
346     }
347 
348     /*
349      * Find whether all required media codec types are supported
350      */
areRequiredCodecTypesSupported( List<CodecType> requiredList, List<CodecType> supportedList)351     private boolean areRequiredCodecTypesSupported(
352         List<CodecType> requiredList, List<CodecType> supportedList) {
353         for (CodecType requiredCodec: requiredList) {
354             boolean isSupported = false;
355             for (CodecType supportedCodec: supportedList) {
356                 if (requiredCodec.equals(supportedCodec)) {
357                     isSupported = true;
358                 }
359             }
360             if (!isSupported) {
361                 String codec = requiredCodec.mMimeTypeName
362                                 + ", " + (requiredCodec.mIsEncoder? "encoder": "decoder");
363                 Log.e(TAG, "Media codec (" + codec + ") is not supported");
364                 return false;
365             }
366         }
367         return true;
368     }
369 
370     /*
371      * Find all the media codec types are supported.
372      */
getSupportedCodecTypes()373     private List<CodecType> getSupportedCodecTypes() {
374         List<CodecType> supportedList = new ArrayList<CodecType>();
375         for (MediaCodecInfo info : mRegularInfos) {
376             String[] types = info.getSupportedTypes();
377             assertTrue("Unexpected number of supported types", types.length > 0);
378             boolean isEncoder = info.isEncoder();
379             for (int j = 0; j < types.length; ++j) {
380                 supportedList.add(new CodecType(types[j], isEncoder, null /* sampleFormat */));
381             }
382         }
383         return supportedList;
384     }
385 
386     /*
387      * This list should be kept in sync with the CCD document
388      * See http://developer.android.com/guide/appendix/media-formats.html
389      */
getRequiredCodecTypes()390     private List<CodecType> getRequiredCodecTypes() {
391         List<CodecType> list = new ArrayList<CodecType>(16);
392 
393         // Mandatory audio decoders
394 
395         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, false, 48000));
396         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 8000));  // mp3
397         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 48000)); // mp3
398         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 8000));
399         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 48000));
400         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 8000));
401         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 48000));
402         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 8000));
403         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 44100));
404         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_OPUS, false, 48000));
405 
406         // Mandatory audio encoders (for non-watch devices with camera)
407 
408         if (hasMicrophone() && !isWatch()) {
409             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 8000));
410             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 48000));
411             // flac encoder is not required
412             // list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, true));  // encoder
413         }
414 
415         // Mandatory audio encoders for handheld devices
416         if (isHandheld()) {
417             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, false, 8000));  // decoder
418             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, true,  8000));  // encoder
419             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, false, 16000)); // decoder
420             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, true,  16000)); // encoder
421         }
422 
423         // Mandatory video codecs (for non-watch devices)
424 
425         if (!isWatch()) {
426             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, false));   // avc decoder
427             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, true));    // avc encoder
428             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, false));   // vp8 decoder
429             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, true));    // vp8 encoder
430             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP9, false));   // vp9 decoder
431 
432             // According to CDD, hevc decoding is not mandatory for automotive and PC devices.
433             if (!isAutomotive() && !isPC()) {
434                 list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_HEVC, false));  // hevc decoder
435             }
436             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_MPEG4, false)); // m4v decoder
437             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, false));  // h263 decoder
438             if (hasCamera()) {
439                 list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, true)); // h263 encoder
440             }
441         }
442 
443         return list;
444     }
445 
testFindDecoderWithAacProfile()446     public void testFindDecoderWithAacProfile() throws Exception {
447         Log.d(TAG, "testFindDecoderWithAacProfile");
448         MediaFormat format = MediaFormat.createAudioFormat(
449                 MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1);
450         List<Integer> profiles = new ArrayList<>();
451         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectLC);
452         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE);
453         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE_PS);
454         // The API is added at 5.0, so the profile below must be supported.
455         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectELD);
456         for (int profile : profiles) {
457             format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
458             String codecName = mRegularCodecs.findDecoderForFormat(format);
459             assertNotNull("Profile " + profile + " must be supported.", codecName);
460         }
461     }
462 
testFindEncoderWithAacProfile()463     public void testFindEncoderWithAacProfile() throws Exception {
464         Log.d(TAG, "testFindEncoderWithAacProfile");
465         MediaFormat format = MediaFormat.createAudioFormat(
466                 MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1);
467         List<Integer> profiles = new ArrayList<>();
468         if (hasMicrophone() && !isWatch()) {
469             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectLC);
470             // The API is added at 5.0, so the profiles below must be supported.
471             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE);
472             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectELD);
473         }
474         for (int profile : profiles) {
475             format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
476             String codecName = mRegularCodecs.findEncoderForFormat(format);
477             assertNotNull("Profile " + profile + " must be supported.", codecName);
478         }
479     }
480 
testAudioCodecChannels()481     public void testAudioCodecChannels() {
482         for (MediaCodecInfo info : mAllInfos) {
483             String[] types = info.getSupportedTypes();
484             for (int j = 0; j < types.length; ++j) {
485                 CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
486                 AudioCapabilities audioCap = cap.getAudioCapabilities();
487                 if (audioCap == null) {
488                     assertFalse("no audio capabilities for audio media type " + types[j] + " of "
489                                     + info.getName(),
490                                 types[j].toLowerCase().startsWith("audio/"));
491                     continue;
492                 }
493                 int n = audioCap.getMaxInputChannelCount();
494                 Log.d(TAG, info.getName() + ": " + n);
495                 assertTrue(info.getName() + " max input channel not positive: " + n, n > 0);
496             }
497         }
498     }
499 
testInputChannelLimits()500     public void testInputChannelLimits() throws IOException {
501         if (!MediaUtils.check(sIsAtLeastS, "testInputChannelLimits invalid before Android 12")) {
502             return;
503         }
504         for (MediaCodecInfo info : mAllInfos) {
505             boolean isEncoder = info.isEncoder();
506             if (!isEncoder) {
507                 continue;
508             }
509             for (String mime: info.getSupportedTypes()) {
510                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
511                 // it advertised this mime, it should have appropriate capabilities
512                 assertNotNull("codec=" + info.getName()
513                               + " no capabilities for advertised mime=" + mime, caps);
514                 AudioCapabilities acaps = caps.getAudioCapabilities();
515                 boolean isAudio = (acaps != null);
516 
517                 if (!isAudio) {
518                     continue;
519                 }
520 
521                 int countMin = acaps.getMinInputChannelCount();
522                 int countMax = acaps.getMaxInputChannelCount();
523                 Range<Integer>[] countRanges = acaps.getInputChannelCountRanges();
524 
525                 assertNotNull("getInputChannelCountRanges() null, codec=" + info.getName(),
526                                 countRanges);
527                 assertTrue("getInputChannelCountRanges() empty range codec=" + info.getName(),
528                                 countRanges.length > 0);
529 
530                 assertEquals("first range lower != min mismatch codec=" + info.getName(),
531                                 countMin, countRanges[0].getLower().intValue());
532                 assertEquals("last range upper != max mismatch codec=" + info.getName(),
533                                 countMax, countRanges[countRanges.length-1].getUpper().intValue());
534 
535                 int foundLow = Integer.MAX_VALUE;
536                 int foundHigh = Integer.MIN_VALUE;
537                 for (Range<Integer> oneRange: countRanges) {
538                     int upper = oneRange.getUpper().intValue();
539                     if (foundHigh < upper) {
540                         foundHigh = upper;
541                     }
542                     int lower = oneRange.getLower().intValue();
543                     if (foundLow > lower) {
544                         foundLow = lower;
545                     }
546                     assertTrue(lower <= upper);
547                 }
548                 assertEquals("minimum count mismatch codec=" + info.getName(),
549                                 countMin, foundLow);
550                 assertEquals("maximum count mismatch codec=" + info.getName(),
551                                 countMax, foundHigh);
552             }
553         }
554     }
555 
556 
557 
testCanonicalCodecIsNotAnAlias(String canonicalName)558     private void testCanonicalCodecIsNotAnAlias(String canonicalName) {
559         // canonical name must point to a non-alias
560         for (MediaCodecInfo canonical : mAllInfos) {
561             if (canonical.getName().equals(canonicalName)) {
562                 assertFalse(canonical.isAlias());
563                 return;
564             }
565         }
566         fail("could not find info to canonical name '" + canonicalName + "'");
567     }
568 
getCustomPartOfComponentName(MediaCodecInfo info)569     private String getCustomPartOfComponentName(MediaCodecInfo info) {
570         String name = info.getName();
571         if (name.startsWith("OMX.") || name.startsWith("c2.")) {
572             // strip off OMX.<vendor_name>.
573             return name.replaceFirst("^OMX\\.([^.]+)\\.", "");
574         }
575         return name;
576     }
577 
testKindInCodecNamesIsMeaningful(MediaCodecInfo info)578     private void testKindInCodecNamesIsMeaningful(MediaCodecInfo info) {
579         String name = getCustomPartOfComponentName(info);
580         // codec names containing 'encoder' or 'enc' must be encoders, 'decoder' or 'dec' must
581         // be decoders
582         if (name.matches("(?i)\\b(encoder|enc)\\b")) {
583             assertTrue(info.isEncoder());
584         }
585         if (name.matches("(?i)\\b(decoder|dec)\\b")) {
586             assertFalse(info.isEncoder());
587         }
588     }
589 
testMediaTypeInCodecNamesIsMeaningful(MediaCodecInfo info)590     private void testMediaTypeInCodecNamesIsMeaningful(MediaCodecInfo info) {
591         // Codec names containing media type names must support that media type
592         String name = getCustomPartOfComponentName(info);
593 
594         Set<String> supportedTypes = new HashSet<String>(Arrays.asList(info.getSupportedTypes()));
595 
596         // video types
597         if (name.matches("(?i)\\b(mp(eg)?2)\\b")) {
598             // this may refer to audio mpeg1-layer2 or video mpeg2
599             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_MPEG2)
600                         || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_MPEG + "-L2"));
601         }
602         if (name.matches("(?i)\\b(h\\.?263)\\b")) {
603             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_H263));
604         }
605         if (name.matches("(?i)\\b(mp(eg)?4)\\b")) {
606             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_MPEG4));
607         }
608         if (name.matches("(?i)\\b(h\\.?264|avc)\\b")) {
609             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_AVC));
610         }
611         if (name.matches("(?i)\\b(vp8)\\b")) {
612             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP8));
613         }
614         if (name.matches("(?i)\\b(h\\.?265|hevc)\\b")) {
615             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_HEVC));
616         }
617         if (name.matches("(?i)\\b(vp9)\\b")) {
618             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP9));
619         }
620         if (name.matches("(?i)\\b(av0?1)\\b")) {
621             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_AV1));
622         }
623 
624         // audio types
625         if (name.matches("(?i)\\b(mp(eg)?3)\\b")) {
626             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_MPEG));
627         }
628         if (name.matches("(?i)\\b(x?aac)\\b")) {
629             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AAC));
630         }
631         if (name.matches("(?i)\\b(pcm)\\b")) {
632             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_RAW));
633         }
634         if (name.matches("(?i)\\b(raw)\\b")) {
635             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_RAW)
636                         || supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_RAW));
637         }
638         if (name.matches("(?i)\\b(amr)\\b")) {
639             if (name.matches("(?i)\\b(nb)\\b")) {
640                 assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_NB));
641             } else if (name.matches("(?i)\\b(wb)\\b")) {
642                 assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_WB));
643             } else {
644                 assertTrue(
645                     supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
646                             || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_WB));
647             }
648         }
649         if (name.matches("(?i)\\b(opus)\\b")) {
650             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_OPUS));
651         }
652         if (name.matches("(?i)\\b(vorbis)\\b")) {
653             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_VORBIS));
654         }
655         if (name.matches("(?i)\\b(flac)\\b")) {
656             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_FLAC));
657         }
658         if (name.matches("(?i)\\b(ac3)\\b")) {
659             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AC3));
660         }
661         if (name.matches("(?i)\\b(ac4)\\b")) {
662             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AC4));
663         }
664         if (name.matches("(?i)\\b(eac3)\\b")) {
665             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_EAC3)
666                         || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC));
667         }
668     }
669 
testCodecCharacterizations()670     public void testCodecCharacterizations() {
671         for (MediaCodecInfo info : mAllInfos) {
672             Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName());
673 
674             // AOSP codecs must not be marked as vendor or hardware accelerated
675             if (info.getName().startsWith("OMX.google.")) {
676                 assertFalse(info.isVendor());
677                 assertFalse(info.isHardwareAccelerated());
678             }
679 
680             // Codec 2.0 based AOSP codecs must run in a software-only process
681             if (info.getName().startsWith("c2.android.")) {
682                 assertTrue(info.isSoftwareOnly());
683                 assertFalse(info.isVendor());
684                 assertFalse(info.isHardwareAccelerated());
685             }
686 
687             // validate aliases
688             if (info.isAlias()) {
689                 assertFalse(info.getName().equals(info.getCanonicalName()));
690                 testCanonicalCodecIsNotAnAlias(info.getCanonicalName());
691             } else {
692                 // validate codec names: (Canonical) codec names must be meaningful.
693                 // We only test this on canonical infos as we allow aliases to support
694                 // existing codec names that do not fit this.
695                 assertEquals(info.getName(), info.getCanonicalName());
696                 testKindInCodecNamesIsMeaningful(info);
697                 testMediaTypeInCodecNamesIsMeaningful(info);
698             }
699 
700             // hardware accelerated codecs cannot be software only
701             assertFalse(info.isHardwareAccelerated() && info.isSoftwareOnly());
702         }
703     }
704 
testVideoPerformancePointsSanity()705     public void testVideoPerformancePointsSanity() {
706         MediaFormat hd25Format =
707             MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720);
708         hd25Format.setFloat(MediaFormat.KEY_FRAME_RATE, 25.f);
709 
710         MediaFormat portraitHd240Format =
711             MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 720, 1280);
712         portraitHd240Format.setInteger(MediaFormat.KEY_FRAME_RATE, 240);
713 
714         /* common-sense checks */
715         assertTrue(VideoCapabilities.PerformancePoint.HD_30.covers(hd25Format));
716         assertTrue(VideoCapabilities.PerformancePoint.HD_25.covers(hd25Format));
717         assertFalse(VideoCapabilities.PerformancePoint.HD_24.covers(hd25Format));
718         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(hd25Format));
719         assertTrue(VideoCapabilities.PerformancePoint.FHD_25.covers(hd25Format));
720         assertFalse(VideoCapabilities.PerformancePoint.FHD_24.covers(hd25Format));
721 
722         assertTrue(VideoCapabilities.PerformancePoint.HD_240.covers(portraitHd240Format));
723         assertFalse(VideoCapabilities.PerformancePoint.HD_200.covers(portraitHd240Format));
724         assertTrue(VideoCapabilities.PerformancePoint.FHD_240.covers(portraitHd240Format));
725         assertFalse(VideoCapabilities.PerformancePoint.FHD_200.covers(portraitHd240Format));
726 
727         /* test macroblock size and conversion support */
728         VideoCapabilities.PerformancePoint bigBlockFHD30_120 =
729             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(128, 64));
730         assertEquals(120, bigBlockFHD30_120.getMaxFrameRate());
731         assertEquals(8160, bigBlockFHD30_120.getMaxMacroBlocks());
732         assertEquals(244800, bigBlockFHD30_120.getMaxMacroBlockRate());
733 
734         VideoCapabilities.PerformancePoint bigRotBlockFHD30_120 =
735             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(64, 128));
736         assertEquals(120, bigRotBlockFHD30_120.getMaxFrameRate());
737         assertEquals(8640, bigRotBlockFHD30_120.getMaxMacroBlocks());
738         assertEquals(259200, bigRotBlockFHD30_120.getMaxMacroBlockRate());
739 
740         /* test conversion logic */
741         {
742             /* 900*900@25-50 */
743             VideoCapabilities.PerformancePoint unusual =
744                 new VideoCapabilities.PerformancePoint(900, 900, 25, 50, new Size(1, 1));
745             assertEquals(50, unusual.getMaxFrameRate());
746             assertEquals(3249, unusual.getMaxMacroBlocks());
747             assertEquals(81225, unusual.getMaxMacroBlockRate());
748 
749             /* becomes 960*1024@25-50 */
750             VideoCapabilities.PerformancePoint converted1 =
751                 new VideoCapabilities.PerformancePoint(unusual, new Size(128, 64));
752             assertEquals(50, converted1.getMaxFrameRate());
753             assertEquals(3840, converted1.getMaxMacroBlocks());
754             assertEquals(96000, converted1.getMaxMacroBlockRate());
755 
756             /* becomes 1024*960@25-50 */
757             VideoCapabilities.PerformancePoint converted2 =
758                 new VideoCapabilities.PerformancePoint(unusual, new Size(64, 128));
759             assertEquals(50, converted2.getMaxFrameRate());
760             assertEquals(3840, converted2.getMaxMacroBlocks());
761             assertEquals(96000, converted2.getMaxMacroBlockRate());
762 
763             /* becomes 1024*1024@25-50 */
764             VideoCapabilities.PerformancePoint converted3 =
765                 new VideoCapabilities.PerformancePoint(converted1, new Size(64, 128));
766             assertEquals(50, converted3.getMaxFrameRate());
767             assertEquals(4096, converted3.getMaxMacroBlocks());
768             assertEquals(102400, converted3.getMaxMacroBlockRate());
769 
770             assertEquals(converted1, converted2);
771             assertEquals(converted2, converted1);
772             assertEquals(converted1, converted3);
773             assertEquals(converted3, converted1);
774             assertTrue(converted1.covers(converted2));
775             assertTrue(converted2.covers(converted1));
776             assertTrue(converted2.covers(converted3));
777             assertTrue(converted3.covers(converted2));
778         }
779 
780         // big macroblock size does not impact standard performance points as the dimensions are set
781         VideoCapabilities.PerformancePoint bigBlockFHD30 =
782             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 30, new Size(128, 64));
783 
784         assertTrue(bigBlockFHD30.covers(VideoCapabilities.PerformancePoint.FHD_30));
785         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(bigBlockFHD30));
786         assertTrue(bigBlockFHD30.equals(VideoCapabilities.PerformancePoint.FHD_30));
787         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.equals(bigBlockFHD30));
788 
789         // but it impacts the case where dimensions differ
790         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1080, 1920, 30)));
791         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1936, 1072, 30)));
792         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1280, 720, 63)));
793         assertTrue(bigBlockFHD30_120.covers(new VideoCapabilities.PerformancePoint(1280, 720, 63)));
794         assertFalse(bigBlockFHD30_120.covers(new VideoCapabilities.PerformancePoint(1280, 720, 64)));
795         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(
796                 new VideoCapabilities.PerformancePoint(1080, 1920, 30)));
797         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(
798                 new VideoCapabilities.PerformancePoint(1936, 1072, 30)));
799         assertTrue(new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(1, 1))
800                    .covers(new VideoCapabilities.PerformancePoint(1280, 720, 68)));
801     }
802 
verifyPerformancePoints( MediaCodecInfo info, String mediaType, List<VideoCapabilities.PerformancePoint> points)803     private void verifyPerformancePoints(
804             MediaCodecInfo info, String mediaType,
805             List<VideoCapabilities.PerformancePoint> points) {
806         List<VideoCapabilities.PerformancePoint> standardPoints = Arrays.asList(
807                 VideoCapabilities.PerformancePoint.UHD_240,
808                 VideoCapabilities.PerformancePoint.UHD_200,
809                 VideoCapabilities.PerformancePoint.UHD_120,
810                 VideoCapabilities.PerformancePoint.UHD_100,
811                 VideoCapabilities.PerformancePoint.UHD_60,
812                 VideoCapabilities.PerformancePoint.UHD_50,
813                 VideoCapabilities.PerformancePoint.UHD_30,
814                 VideoCapabilities.PerformancePoint.UHD_25,
815                 VideoCapabilities.PerformancePoint.UHD_24,
816                 VideoCapabilities.PerformancePoint.FHD_240,
817                 VideoCapabilities.PerformancePoint.FHD_200,
818                 VideoCapabilities.PerformancePoint.FHD_120,
819                 VideoCapabilities.PerformancePoint.FHD_100,
820                 VideoCapabilities.PerformancePoint.FHD_60,
821                 VideoCapabilities.PerformancePoint.FHD_50,
822                 VideoCapabilities.PerformancePoint.FHD_30,
823                 VideoCapabilities.PerformancePoint.FHD_25,
824                 VideoCapabilities.PerformancePoint.FHD_24,
825                 VideoCapabilities.PerformancePoint.HD_240,
826                 VideoCapabilities.PerformancePoint.HD_200,
827                 VideoCapabilities.PerformancePoint.HD_120,
828                 VideoCapabilities.PerformancePoint.HD_100,
829                 VideoCapabilities.PerformancePoint.HD_60,
830                 VideoCapabilities.PerformancePoint.HD_50,
831                 VideoCapabilities.PerformancePoint.HD_30,
832                 VideoCapabilities.PerformancePoint.HD_25,
833                 VideoCapabilities.PerformancePoint.HD_24,
834                 VideoCapabilities.PerformancePoint.SD_60,
835                 VideoCapabilities.PerformancePoint.SD_50,
836                 VideoCapabilities.PerformancePoint.SD_48,
837                 VideoCapabilities.PerformancePoint.SD_30,
838                 VideoCapabilities.PerformancePoint.SD_25,
839                 VideoCapabilities.PerformancePoint.SD_24);
840 
841         // Components must list all supported standard performance points unless those performance
842         // points are covered by other listed standard performance points.
843         for (VideoCapabilities.PerformancePoint pp : points) {
844             if (standardPoints.contains(pp)) {
845                 // standard points must not be covered by other listed standard points
846                 for (VideoCapabilities.PerformancePoint pp2 : points) {
847                     if (!standardPoints.contains(pp2)) {
848                         continue;
849                     }
850                     // using object equality to determine otherness
851                     assertFalse("standard " + pp2 + " for " + info.getCanonicalName()
852                             + " for media type " + mediaType + " covers standard " + pp,
853                             pp2 != pp && pp2.covers(pp));
854                 }
855             } else {
856                 // non-standard points must list all covered standard point not covered by another
857                 // listed standard point
858                 for (VideoCapabilities.PerformancePoint spp : standardPoints) {
859                     if (pp.covers(spp)) {
860                         // Must be either listed or covered by another standard. Since a point
861                         // covers itself, it is sufficient to check that it is covered by a listed
862                         // standard point.
863                         boolean covered = false;
864                         for (VideoCapabilities.PerformancePoint pp2 : points) {
865                             // using object equality to determine otherness
866                             if (standardPoints.contains(pp2) && pp2.covers(spp)) {
867                                 covered = true;
868                                 break;
869                             }
870                         }
871                         assertTrue(pp + " for " + info.getCanonicalName() + " for media type "
872                                 + mediaType + " covers standard " + spp
873                                 + " that is not covered by a listed standard point",
874                                 covered);
875                     }
876                 }
877                 // non-standard points should not be covered by any other performance point
878                 for (VideoCapabilities.PerformancePoint pp2 : points) {
879                     // using object equality to determine otherness
880                     assertFalse(pp2 + " for " + info.getCanonicalName()
881                             + " for media type " + mediaType + " covers " + pp,
882                             pp2 != pp && pp2.covers(pp));
883                 }
884             }
885         }
886     }
887 
testAllHardwareAcceleratedVideoCodecsPublishPerformancePoints()888     public void testAllHardwareAcceleratedVideoCodecsPublishPerformancePoints() {
889         List<String> mandatoryTypes = Arrays.asList(
890                 MediaFormat.MIMETYPE_VIDEO_AVC,
891                 MediaFormat.MIMETYPE_VIDEO_VP8,
892                 MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION,
893                 MediaFormat.MIMETYPE_VIDEO_HEVC,
894                 MediaFormat.MIMETYPE_VIDEO_VP9,
895                 MediaFormat.MIMETYPE_VIDEO_AV1);
896 
897         String[] featuresToConfig = new String[] {
898             FEATURE_SecurePlayback,
899             FEATURE_TunneledPlayback,
900         };
901 
902         Set<Pair<String, Integer>> describedTypes = new HashSet<>(); // mediaType - featureIndex
903         Set<Pair<String, Integer>> supportedTypes = new HashSet<>(); // mediaType - featureIndex
904 
905         // Once any hardware codec performance is described, we assume that all hardware codecs
906         // must be described, even if we cannot confirm expanded codec info support.
907         boolean hasPerformancePoints = hasExpandedCodecInfo();
908         if (!hasPerformancePoints) {
909             for (MediaCodecInfo info : mAllInfos) {
910                 String[] types = info.getSupportedTypes();
911                 for (int j = 0; j < types.length; ++j) {
912                     String mediaType = types[j];
913                     CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
914                     VideoCapabilities videoCap = cap.getVideoCapabilities();
915                     if (videoCap != null
916                             && videoCap.getSupportedPerformancePoints() != null) {
917                         hasPerformancePoints = true;
918                         break;
919                     }
920                 }
921                 if (hasPerformancePoints) {
922                     break;
923                 }
924             }
925         }
926 
927         for (MediaCodecInfo info : mAllInfos) {
928             String[] types = info.getSupportedTypes();
929             for (int j = 0; j < types.length; ++j) {
930                 String mediaType = types[j];
931                 CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
932                 MediaFormat defaultFormat = cap.getDefaultFormat();
933                 VideoCapabilities videoCap = cap.getVideoCapabilities();
934 
935                 Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName()
936                         + " type: " + mediaType);
937 
938                 if (videoCap == null) {
939                     assertFalse("no video capabilities for video media type " + mediaType + " of "
940                                     + info.getName(),
941                                 mediaType.toLowerCase().startsWith("video/"));
942                     continue;
943                 }
944 
945                 List<VideoCapabilities.PerformancePoint> pps =
946                     videoCap.getSupportedPerformancePoints();
947 
948                 // see which feature combinations are supported by this codec
949                 // we do this by counting in binary up to a number of bits
950                 List<Integer> supportedFeatureConfigs = new ArrayList<Integer>();
951                 for (int cfg_ix = 1 << featuresToConfig.length; --cfg_ix >= 0; ) {
952                     boolean supported = true;
953                     for (int f_ix = 0; supported && f_ix < featuresToConfig.length; ++f_ix) {
954                         if (((cfg_ix >> f_ix) & 1) != 0) {
955                             // feature is to be enabled
956                             supported = supported && cap.isFeatureSupported(featuresToConfig[f_ix]);
957                         } else {
958                             // feature is not to be enabled
959                             supported = supported && !cap.isFeatureRequired(featuresToConfig[f_ix]);
960                         }
961                     }
962                     if (supported) {
963                         supportedFeatureConfigs.add(cfg_ix);
964                     }
965                 }
966 
967                 Log.d(TAG, "codec supports configs " + Arrays.toString(
968                         supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray()));
969                 boolean isMandatory = mandatoryTypes.contains(mediaType);
970                 if (info.isHardwareAccelerated()) {
971                     for (Integer cfg_ix : supportedFeatureConfigs) {
972                         Pair<String, Integer> type = Pair.create(mediaType, cfg_ix);
973                         if (hasPerformancePoints && isMandatory) {
974                             supportedTypes.add(type);
975                         }
976                         if (pps != null && pps.size() > 0) {
977                             describedTypes.add(type);
978                         }
979                     }
980                 }
981 
982                 if (pps == null) {
983                     if (hasExpandedCodecInfo()) {
984                         // Hardware-accelerated video components must publish performance points,
985                         // even if it is an empty list.
986                         assertFalse("HW-accelerated codec '" + info.getName()
987                                 + "' must publish performance points", info.isHardwareAccelerated());
988                     }
989 
990                     continue;
991                 }
992 
993                 // All hardware accelerated codecs must publish performance points
994                 if (pps.size() == 0) {
995                     assertFalse("all hw codecs must advertise perf points",
996                             info.isHardwareAccelerated());
997                     continue;
998                 }
999                 for (VideoCapabilities.PerformancePoint p : pps) {
1000                     Log.d(TAG, "got performance point " + p);
1001                 }
1002                 verifyPerformancePoints(info, types[j], pps);
1003             }
1004         }
1005 
1006         for (Pair<String, Integer> type : supportedTypes) {
1007             assertTrue("codecs for media type " + type.first + " in configuration " + type.second
1008                     + " do not have substantial performance point data",
1009                     describedTypes.contains(type));
1010         }
1011     }
1012 }
1013