CtsMediaBitstreamsTestCases: refactor metric entry format

Log test results in the following format for simpler data analysis:
{
  "path": "path/to/bitstream",
  "codec_name": "omx.decoder.name",
  "status": "unsupported|true|false|crash|timeout"
}

Bug: 30268664
Test: cts-tradefed run cts -m CtsMediaBitstreamsTestCases
Change-Id: I3d6c38dd50f839d422e5a11ec523958889fcd5bf
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
index 1727a3a..c7d2fa5 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
@@ -245,6 +245,18 @@
         return getCodecNames(true /* isEncoder */, null /* isGoog */, formats);
     }
 
+    public static String[] getDecoderNamesForMime(String mime) {
+        MediaFormat format = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, mime);
+        return getCodecNames(false /* isEncoder */, null /* isGoog */, format);
+    }
+
+    public static String[] getEncoderNamesForMime(String mime) {
+        MediaFormat format = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, mime);
+        return getCodecNames(true /* isEncoder */, null /* isGoog */, format);
+    }
+
     public static void verifyNumCodecs(
             int count, boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
         String desc = (isEncoder ? "encoders" : "decoders") + " for "
diff --git a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
index 92f0d2a..32fc86b 100644
--- a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
+++ b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
@@ -21,6 +21,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
 import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.cts.bitstreams.MediaBitstreams;
@@ -161,12 +162,18 @@
                 }
 
                 MediaFormat format = parseTrackFormat(formatStr);
-                if (!MediaUtils.checkDecoderForFormat(format)) {
-                    continue;
-                }
+                String mime = format.getString(MediaFormat.KEY_MIME);
+                String[] decoders = MediaUtils.getDecoderNamesForMime(mime);
 
                 ps.println(path);
+                ps.println(decoders.length);
+                for (String name : decoders) {
+                    ps.println(name);
+                    ps.println(MediaUtils.supports(name, format));
+                }
+
             }
+
             ps.flush();
         }
     }
diff --git a/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java b/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
index 8b9a507..b4ab76c 100644
--- a/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
+++ b/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
@@ -38,17 +38,21 @@
     public static final String DEFAULT_DEVICE_BITSTEAMS_PATH = "/data/local/tmp/TestVectorsIttiam";
 
     /* metric keys */
-    public static final String KEY_BITSTREAMS_FORMATS_XML = "bitstreams-formats-xml";
-    public static final String KEY_SUPPORTED_BITSTREAMS_TXT = "supported-bitstreams-txt";
-    public static final String KEY_BITSTREAMS_VALIDATION_TXT = "bitstreams-validation-txt";
-    public static final String KEY_APP_CACHE_DIR = "app-cache-dir";
-    public static final String KEY_ERR_MSG = "err-msg";
+    public static final String KEY_BITSTREAMS_FORMATS_XML = "bitstreams_formats_xml";
+    public static final String KEY_SUPPORTED_BITSTREAMS_TXT = "supported_bitstreams_txt";
+    public static final String KEY_BITSTREAMS_VALIDATION_TXT = "bitstreams_validation_txt";
+    public static final String KEY_APP_CACHE_DIR = "app_cache_dir";
+    public static final String KEY_ERR_MSG = "err_msg";
+    public static final String KEY_PATH = "path";
+    public static final String KEY_CODEC_NAME = "codec_name";
+    public static final String KEY_STATUS = "status";
 
     /* constants */
     public static final String K_MODULE = "CtsMediaBitstreamsTestCases";
     public static final String K_BITSTREAMS_LIST_TXT = "bitstreamsFile.txt";
     public static final String K_TEST_GET_SUPPORTED_BITSTREAMS = "testGetSupportedBitstreams";
     public static final String K_NATIVE_CRASH = "native crash";
+    public static final String K_UNSUPPORTED = "unsupported";
 
     public static final String DYNAMIC_CONFIG_XML = "DynamicConfig.xml";
     public static final String DYNAMIC_CONFIG = "dynamicConfig";
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
index a82b9c3..0da0193 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
@@ -46,6 +46,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -168,6 +169,29 @@
         return Thread.currentThread().getStackTrace()[2].getMethodName();
     }
 
+    private MetricsReportLog createReport(String methodName) {
+        String className = MediaBitstreamsTest.class.getCanonicalName();
+        MetricsReportLog report = new MetricsReportLog(
+                mBuildHelper.getBuildInfo(), mAbi.getName(),
+                String.format("%s#%s", className, methodName),
+                MediaBitstreams.K_MODULE, "media_bitstreams_conformance");
+        return report;
+    }
+
+    /**
+     * @param method test method name in the form class#method
+     * @param p path to bitstream
+     * @param d decoder name
+     * @param s test status: unsupported, true, false, crash, or timeout.
+     */
+    private void addConformanceEntry(String method, String p, String d, String s) {
+        MetricsReportLog report = createReport(method);
+        report.addValue(MediaBitstreams.KEY_PATH, p, ResultType.NEUTRAL, ResultUnit.NONE);
+        report.addValue(MediaBitstreams.KEY_CODEC_NAME, d, ResultType.NEUTRAL, ResultUnit.NONE);
+        report.addValue(MediaBitstreams.KEY_STATUS, s, ResultType.NEUTRAL, ResultUnit.NONE);
+        report.submit();
+    }
+
     Map<String, String> getArgs() {
         Map<String, String> args = new HashMap<>();
         args.put(MediaBitstreams.OPT_DEBUG_TARGET_DEVICE, Boolean.toString(mDebugTargetDevice));
@@ -274,14 +298,9 @@
 
                 String path = lines[i++];
                 mProcessedBitstreams.add(path);
-                String className = MediaBitstreamsTest.class.getCanonicalName();
-                MetricsReportLog report = new MetricsReportLog(
-                        mBuildHelper.getBuildInfo(), mAbi.getName(),
-                        String.format("%s#%s", className, mMethodName),
-                        getClass().getSimpleName(), path.replaceAll("[./]", "_"));
+                String errMsg;
 
                 boolean failedEarly;
-                String errMsg;
                 if (i < lines.length) {
                     failedEarly = Boolean.parseBoolean(lines[i++]);
                     errMsg = failedEarly ? lines[i++] : "";
@@ -291,27 +310,26 @@
                     mLastCrash = MediaBitstreams.generateCrashSignature(path, "");
                     mProcessedBitstreams.removeLast();
                 }
+
                 if (failedEarly) {
-                    String keyErrMsg = MediaBitstreams.KEY_ERR_MSG;
-                    report.addValue(keyErrMsg, errMsg, ResultType.NEUTRAL, ResultUnit.NONE);
-                    report.submit();
+                    addConformanceEntry(mMethodName, path, null, errMsg);
                     continue;
                 }
 
                 int n = Integer.parseInt(lines[i++]);
                 for (int j = 0; j < n && i < lines.length; j++) {
-                    String name = lines[i++];
+                    String decoderName = lines[i++];
                     String result;
                     if (i < lines.length) {
                         result = lines[i++];
                     } else {
                         result = MediaBitstreams.K_NATIVE_CRASH;
-                        mLastCrash = MediaBitstreams.generateCrashSignature(path, name);
+                        mLastCrash = MediaBitstreams.generateCrashSignature(path, decoderName);
                         mProcessedBitstreams.removeLast();
                     }
-                    report.addValue(name, result, ResultType.NEUTRAL, ResultUnit.NONE);
+                    addConformanceEntry(mMethodName, path, decoderName, result);
                 }
-                report.submit();
+
 
             }
         }
@@ -365,6 +383,7 @@
                 device,
                 MediaBitstreams.K_TEST_GET_SUPPORTED_BITSTREAMS,
                 MediaBitstreams.KEY_SUPPORTED_BITSTREAMS_TXT);
+        Set<String> bitstreams = preparer.getBitstreams();
         Set<String> supportedBitstreams = preparer.getSupportedBitstreams();
         CLog.i("%d supported bitstreams under %s", supportedBitstreams.size(), prefix);
 
@@ -372,35 +391,43 @@
         long size = 0;
         long limit = device.getExternalStoreFreeSpace() * mUtilizationRate * 1024 / 100;
 
-        String currentMethod = getCurrentMethod();
-        Set<String> bitstreams = new LinkedHashSet<>();
-        Iterator<String> iter = supportedBitstreams.iterator();
+        String curMethod = getCurrentMethod();
+        Set<String> toPush = new LinkedHashSet<>();
+        Iterator<String> iter = bitstreams.iterator();
 
-        for (int i = 0; i < supportedBitstreams.size(); i++) {
+        for (int i = 0; i < bitstreams.size(); i++) {
 
             if (n >= mNumBatches) {
                 break;
             }
 
-            String bitstreamPath = iter.next();
-            File bitstreamFile = new File(mHostBitstreamsPath, bitstreamPath);
-            String md5Path = MediaBitstreams.getMd5Path(bitstreamPath);
-            File md5File = new File(mHostBitstreamsPath, md5Path);
-
-            if (md5File.exists() && bitstreamFile.exists()) {
-                size += md5File.length();
-                size += bitstreamFile.length();
-                bitstreams.add(bitstreamPath);
+            String p = iter.next();
+            Map<String, Boolean> decoderCapabilities;
+            decoderCapabilities = preparer.getDecoderCapabilitiesForPath(p);
+            for (Entry<String, Boolean> entry : decoderCapabilities.entrySet()) {
+                Boolean supported = entry.getValue();
+                if (supported) {
+                    File bitstreamFile = new File(mHostBitstreamsPath, p);
+                    String md5Path = MediaBitstreams.getMd5Path(p);
+                    File md5File = new File(mHostBitstreamsPath, md5Path);
+                    if (md5File.exists() && bitstreamFile.exists() && toPush.add(p)) {
+                        size += md5File.length();
+                        size += bitstreamFile.length();
+                    }
+                } else {
+                    String d = entry.getKey();
+                    addConformanceEntry(curMethod, p, d, MediaBitstreams.K_UNSUPPORTED);
+                }
             }
 
-            if (size > limit || i + 1 == supportedBitstreams.size()) {
+            if (size > limit || i + 1 == bitstreams.size()) {
                 ReportProcessor processor;
-                processor = new ProcessBitstreamsValidation(bitstreams, currentMethod);
+                processor = new ProcessBitstreamsValidation(toPush, curMethod);
                 processor.processDeviceReport(
                         device,
-                        currentMethod,
+                        curMethod,
                         MediaBitstreams.KEY_BITSTREAMS_VALIDATION_TXT);
-                bitstreams.clear();
+                toPush.clear();
                 size = 0;
                 n++;
             }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/SupportedBitstreamsProcessor.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/SupportedBitstreamsProcessor.java
index 8a6c74e..87ccda4 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/SupportedBitstreamsProcessor.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/SupportedBitstreamsProcessor.java
@@ -18,6 +18,8 @@
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
@@ -32,13 +34,14 @@
     private final String mPrefix;
     private final boolean mDebugTargetDevice;
     private final Set<String> mSupportedBitstreams = new LinkedHashSet<>();
+    private final Map<String, Map<String, Boolean>> mDecodersForPath = new HashMap<>();
 
     public SupportedBitstreamsProcessor() {
         this("",false);
     }
 
     /**
-     * @param prefix only bitstreams whose relative path starts with {@code prefix}
+     * @param prefix only bitstreams whose relative paths start with {@code prefix}
      * would be processed
      * @param debugTargetDevice whether to pause {@code device} for debugging
      */
@@ -48,12 +51,26 @@
     }
 
     /**
-     * @return paths of supported devices on device
+     * @return paths to bitstreams that are supported on device
      */
     public Set<String> getSupportedBitstreams() {
         return mSupportedBitstreams;
     }
 
+    /**
+     * @return paths to all bitstreams whose relative paths start with <code>prefix</code>
+     */
+    public Set<String> getBitstreams() {
+        return mDecodersForPath.keySet();
+    }
+
+    public Map<String, Boolean> getDecoderCapabilitiesForPath(String path) {
+        if (mDecodersForPath.containsKey(path)) {
+            return mDecodersForPath.get(path);
+        }
+        return Collections.emptyMap();
+    }
+
     @Override
     Map<String, String> getArgs() {
         Map<String, String> args = new HashMap<>();
@@ -65,11 +82,29 @@
     @Override
     void process(ITestDevice device, String reportPath) throws DeviceNotAvailableException {
         mSupportedBitstreams.clear();
-        for (String path: getReportLines(device, reportPath)) {
-            if (path.isEmpty()) {
-                continue;
+        String[] lines = getReportLines(device, reportPath);
+        try {
+            for (int i = 0; i < lines.length;) {
+                String path = lines[i++];
+                int n = Integer.parseInt(lines[i++]);
+                for (int j = 0; j < n; j++) {
+                    String name = lines[i++];
+                    String status = lines[i++];
+                    boolean supported = status.equals("true");
+                    if (supported) {
+                        mSupportedBitstreams.add(path);
+                    }
+                    Map<String, Boolean> decoderCapabilities;
+                    if (mDecodersForPath.containsKey(path)) {
+                        decoderCapabilities = mDecodersForPath.get(path);
+                    } else {
+                        mDecodersForPath.put(path, decoderCapabilities = new HashMap<>());
+                    }
+                    decoderCapabilities.put(name, supported);
+                }
             }
-            mSupportedBitstreams.add(path);
+        } catch (Exception e) {
+            CLog.w(e);
         }
     }