AudioLoopback bug fixes

b/63096300: Fixed problem with reported data to Blackbox - field name
was missing prefix ("48000_Mic3_") so the fields were treated as completely
new fields.

b/63096300: Never seen this on my host, but in the lab I'm getting
nullpointer exception when trying to open log files
(code fix, AudioLoopbackTest, line 640 + line 692)

b/63104260: In all exitement of creating a new test, I forgot to
assemble the test data (metrics) and pass up to test harness.
(added function "populateStressTestMetrics")

b/63109598: Enabled more logfiles in array
AudioLoopbackTest.LATENCY_TEST_LOGS for Latency test.

Bug: 63096300
Bug: 63074048
Bug: 63104260
Bug: 63109598
Fixes:
Test: tradefed.sh run google/test/framework/media/audio-loopback-stress
 --iterations 1000 -s <device serial number>

Change-Id: I8026ebbe3b5d4e0133ad234fc3e99dc0d6bc69da
diff --git a/src/com/android/media/tests/AudioLoopbackTest.java b/src/com/android/media/tests/AudioLoopbackTest.java
index a9c787c..1d9bab5 100644
--- a/src/com/android/media/tests/AudioLoopbackTest.java
+++ b/src/com/android/media/tests/AudioLoopbackTest.java
@@ -37,6 +37,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -151,16 +152,20 @@
     private static final String KEY_RESULT_AUDIO_LEVEL = "audio_level";
     private static final String KEY_RESULT_RMS = "rms";
     private static final String KEY_RESULT_RMS_AVERAGE = "rms_average";
-    private static final String KEY_RESULT_SAMPLING_FREQUENCY_CONFIDENCE = "sf";
+    private static final String KEY_RESULT_SAMPLING_FREQUENCY_CONFIDENCE = "sampling_frequency";
     private static final String KEY_RESULT_PERIOD_CONFIDENCE = "period_confidence";
-    private static final String KEY_RESULT_SAMPLING_BLOCK_SIZE = "bs";
+    private static final String KEY_RESULT_SAMPLING_BLOCK_SIZE = "block_size";
 
     private static final LogFileType[] LATENCY_TEST_LOGS = {
         LogFileType.RESULT,
         LogFileType.GRAPH,
         LogFileType.WAVE,
         LogFileType.PLAYER_BUFFER,
+        LogFileType.PLAYER_BUFFER_HISTOGRAM,
+        LogFileType.PLAYER_BUFFER_PERIOD_TIMES,
         LogFileType.RECORDER_BUFFER,
+        LogFileType.RECORDER_BUFFER_HISTOGRAM,
+        LogFileType.RECORDER_BUFFER_PERIOD_TIMES,
         LogFileType.LOGCAT
     };
 
@@ -170,9 +175,12 @@
         LogFileType.WAVE,
         LogFileType.PLAYER_BUFFER,
         LogFileType.PLAYER_BUFFER_HISTOGRAM,
+        LogFileType.PLAYER_BUFFER_PERIOD_TIMES,
         LogFileType.RECORDER_BUFFER,
         LogFileType.RECORDER_BUFFER_HISTOGRAM,
+        LogFileType.RECORDER_BUFFER_PERIOD_TIMES,
         LogFileType.GLITCHES_MILLIS,
+        LogFileType.HEAT_MAP,
         LogFileType.LOGCAT
     };
 
@@ -205,17 +213,31 @@
         l = new LogFileData("_playerBufferPeriod.png", "player_buffer_histogram", LogDataType.PNG);
         result.put(LogFileType.PLAYER_BUFFER_HISTOGRAM, l);
 
+        String fileExtension = "_playerBufferPeriodTimes.txt";
+        String uploadName = "player_buffer_period_times";
+        l = new LogFileData(fileExtension, uploadName, LogDataType.TEXT);
+        result.put(LogFileType.PLAYER_BUFFER_PERIOD_TIMES, l);
+
         l = new LogFileData("_recorderBufferPeriod.txt", "recorder_buffer", LogDataType.TEXT);
         result.put(LogFileType.RECORDER_BUFFER, l);
 
-        l =
-                new LogFileData(
-                        "_recorderBufferPeriod.png", "recorder_buffer_histogram", LogDataType.PNG);
+        fileExtension = "_recorderBufferPeriod.png";
+        uploadName = "recorder_buffer_histogram";
+        l = new LogFileData(fileExtension, uploadName, LogDataType.PNG);
         result.put(LogFileType.RECORDER_BUFFER_HISTOGRAM, l);
 
-        l = new LogFileData("_glitchMillis.txt", "wave", LogDataType.TEXT);
+        fileExtension = "_recorderBufferPeriodTimes.txt";
+        uploadName = "recorder_buffer_period_times";
+        l = new LogFileData(fileExtension, uploadName, LogDataType.TEXT);
+        result.put(LogFileType.RECORDER_BUFFER_PERIOD_TIMES, l);
+
+        l = new LogFileData("_glitchMillis.txt", "glitches_millis", LogDataType.TEXT);
         result.put(LogFileType.GLITCHES_MILLIS, l);
 
+
+        l = new LogFileData("_heatMap.png", "heat_map", LogDataType.PNG);
+        result.put(LogFileType.HEAT_MAP, l);
+
         l = new LogFileData(".txt", "logcat", LogDataType.TEXT);
         result.put(LogFileType.LOGCAT, l);
 
@@ -322,7 +344,9 @@
 
             mLoopbackTestHelper.processTestData();
         } finally {
-            mTestRunHelper.endTest(uploadLogsReturnMetrics(listener));
+            Map<String, String> metrics = uploadLogsReturnMetrics(listener);
+            CLog.i("Uploading metrics values:\n" + Arrays.toString(metrics.entrySet().toArray()));
+            mTestRunHelper.endTest(metrics);
             deleteAllTempFiles();
             getDevice().startLogcat();
         }
@@ -405,7 +429,7 @@
         try {
             loopbackResult =
                     AudioLoopbackTestHelper.parseKeyValuePairFromFile(
-                            loopbackReport, METRICS_KEY_MAP, "=", "%s: %s");
+                            loopbackReport, METRICS_KEY_MAP, mKeyPrefix, "=", "%s: %s");
             populateResultData(loopbackResult, data);
 
             // Trust but verify, so get Audio Level from ADB and compare to value from app
@@ -429,6 +453,9 @@
         return loopbackResult;
     }
 
+    private String getMetricsKey(final String key) {
+        return mKeyPrefix + key;
+    }
     private final long getSingleTestTimeoutValue() {
         return Long.parseLong(mBufferTestDuration) * 1000 + TIMEOUT_MS;
     }
@@ -444,26 +471,42 @@
 
         switch (getTestType()) {
             case GLITCH:
-            case LATENCY:
-                // use dictionary collected from single test run
                 resultDictionary = mLoopbackTestHelper.getResultDictionaryForIteration(0);
+                // Upload all test files to be backward compatible with old test
                 results = mLoopbackTestHelper.getAllTestData();
                 break;
+            case LATENCY:
+                {
+                    final int nrOfValidResults = mLoopbackTestHelper.processTestData();
+                    if (nrOfValidResults == 0) {
+                        mTestRunHelper.reportFailure("No good data was collected");
+                    } else {
+                        // use dictionary collected from single test run
+                        resultDictionary = mLoopbackTestHelper.getResultDictionaryForIteration(0);
+                    }
+
+                    // Upload all test files to be backward compatible with old test
+                    results = mLoopbackTestHelper.getAllTestData();
+                }
+                break;
             case LATENCY_STRESS:
-                try {
-                    saveResultsAsCSVFile(listener);
-                } catch (final IOException e) {
-                    CLog.e(e);
-                }
+                {
+                    final int nrOfValidResults = mLoopbackTestHelper.processTestData();
+                    if (nrOfValidResults == 0) {
+                        mTestRunHelper.reportFailure("No good data was collected");
+                    } else {
+                        mLoopbackTestHelper.populateStressTestMetrics(resultDictionary, mKeyPrefix);
+                    }
 
-                final int nrOfValidResults = mLoopbackTestHelper.processTestData();
-                if (nrOfValidResults == 0) {
-                    mTestRunHelper.reportFailure("No good data was collected");
-                }
+                    results = mLoopbackTestHelper.getWorstResults(MAX_NR_OF_LOG_UPLOADS);
 
-                final String nrOfTestsWithResults = Integer.toString(nrOfValidResults);
-                resultDictionary.put("NrOfTestsWithResults", nrOfTestsWithResults);
-                results = mLoopbackTestHelper.getWorstResults(MAX_NR_OF_LOG_UPLOADS);
+                    // Save all test data in a spreadsheet style csv file for post test analysis
+                    try {
+                        saveResultsAsCSVFile(listener);
+                    } catch (final IOException e) {
+                        CLog.e(e);
+                    }
+                }
                 break;
             default:
                 break;
@@ -541,37 +584,37 @@
             return;
         }
 
-        String key = KEY_RESULT_LATENCY_MS;
+        String key = getMetricsKey(KEY_RESULT_LATENCY_MS);
         if (results.containsKey(key)) {
             data.setLatency(Float.parseFloat(results.get(key)));
         }
 
-        key = KEY_RESULT_LATENCY_CONFIDENCE;
+        key = getMetricsKey(KEY_RESULT_LATENCY_CONFIDENCE);
         if (results.containsKey(key)) {
             data.setConfidence(Float.parseFloat(results.get(key)));
         }
 
-        key = KEY_RESULT_AUDIO_LEVEL;
+        key = getMetricsKey(KEY_RESULT_AUDIO_LEVEL);
         if (results.containsKey(key)) {
             data.setAudioLevel(Integer.parseInt(results.get(key)));
         }
 
-        key = KEY_RESULT_RMS;
+        key = getMetricsKey(KEY_RESULT_RMS);
         if (results.containsKey(key)) {
             data.setRMS(Float.parseFloat(results.get(key)));
         }
 
-        key = KEY_RESULT_RMS_AVERAGE;
+        key = getMetricsKey(KEY_RESULT_RMS_AVERAGE);
         if (results.containsKey(key)) {
             data.setRMSAverage(Float.parseFloat(results.get(key)));
         }
 
-        key = KEY_RESULT_PERIOD_CONFIDENCE;
+        key = getMetricsKey(KEY_RESULT_PERIOD_CONFIDENCE);
         if (results.containsKey(key)) {
             data.setPeriodConfidence(Float.parseFloat(results.get(key)));
         }
 
-        key = KEY_RESULT_SAMPLING_BLOCK_SIZE;
+        key = getMetricsKey(KEY_RESULT_SAMPLING_BLOCK_SIZE);
         if (results.containsKey(key)) {
             data.setBlockSize(Integer.parseInt(results.get(key)));
         }
@@ -594,7 +637,12 @@
         for (final ResultData d : mLoopbackTestHelper.getAllTestData()) {
             final LogFileType[] logFileTypes = getLogFileTypesForCurrentTest();
             for (final LogFileType logType : logFileTypes) {
-                FileUtil.deleteFile(new File(d.getLogFile(logType)));
+                final String logFilename = d.getLogFile(logType);
+                if (logFilename == null || logFilename.isEmpty()) {
+                    CLog.e("Logfile not found for LogFileType=" + logType.name());
+                } else {
+                    FileUtil.deleteFile(new File(logFilename));
+                }
             }
         }
     }
@@ -641,12 +689,17 @@
         final LogFileData logInfo = map.get(key);
         final String prefix = getKeyPrefixForIteration(data.getIteration()) + logInfo.filePrefix;
         final LogDataType logDataType = logInfo.logDataType;
-        File logFile = new File(data.getLogFile(key));
-        InputStreamSource iss = new FileInputStreamSource(logFile);
-        listener.testLog(prefix, logDataType, iss);
+        final String logFilename = data.getLogFile(key);
+        if (logFilename == null || logFilename.isEmpty()) {
+            CLog.e("Logfile not found for LogFileType=" + key.name());
+        } else {
+            File logFile = new File(logFilename);
+            InputStreamSource iss = new FileInputStreamSource(logFile);
+            listener.testLog(prefix, logDataType, iss);
 
-        // cleanup
-        iss.cancel();
+            // cleanup
+            iss.cancel();
+        }
     }
 
     private void saveLogcatForIteration(ResultData data, InputStreamSource logcat, int iteration) {
diff --git a/src/com/android/media/tests/AudioLoopbackTestHelper.java b/src/com/android/media/tests/AudioLoopbackTestHelper.java
index a487d44..4e9c2b0 100644
--- a/src/com/android/media/tests/AudioLoopbackTestHelper.java
+++ b/src/com/android/media/tests/AudioLoopbackTestHelper.java
@@ -62,9 +62,12 @@
         GRAPH,
         PLAYER_BUFFER,
         PLAYER_BUFFER_HISTOGRAM,
+        PLAYER_BUFFER_PERIOD_TIMES,
         RECORDER_BUFFER,
         RECORDER_BUFFER_HISTOGRAM,
+        RECORDER_BUFFER_PERIOD_TIMES,
         GLITCHES_MILLIS,
+        HEAT_MAP,
         LOGCAT
     }
 
@@ -203,6 +206,7 @@
         }
 
         public void setLogFile(LogFileType log, String filename) {
+            CLog.i("setLogFile: type=" + log.name() + ", filename=" + filename);
             if (!mLogs.containsKey(log) && filename != null && !filename.isEmpty()) {
                 mLogs.put(log, filename);
             }
@@ -352,6 +356,7 @@
     public static Map<String, String> parseKeyValuePairFromFile(
             File result,
             final Map<String, String> dictionary,
+            final String resultKeyPrefix,
             final String splitOn,
             final String keyValueFormat)
             throws IOException {
@@ -369,7 +374,7 @@
                     final String value = tokens[1].trim();
                     if (dictionary.containsKey(key)) {
                         CLog.i(String.format(keyValueFormat, key, value));
-                        resultMap.put(dictionary.get(key), value);
+                        resultMap.put(resultKeyPrefix + dictionary.get(key), value);
                     }
                 }
                 line = br.readLine();
@@ -584,4 +589,20 @@
                         "No Confidence results: %1$d of %2$d",
                         testWithoutConfidenceResultCount, totalNrOfTests));
     }
+
+    /** Generates metrics dictionary for stress test */
+    public void populateStressTestMetrics(
+            Map<String, String> metrics, final String resultKeyPrefix) {
+        metrics.put(resultKeyPrefix + "total_nr_of_tests", Integer.toString(mAllResults.size()));
+        metrics.put(resultKeyPrefix + "nr_of_good_tests", Integer.toString(mGoodResults.size()));
+        metrics.put(resultKeyPrefix + "latency_max", Double.toString(mLatencyStats.mMax));
+        metrics.put(resultKeyPrefix + "latency_min", Double.toString(mLatencyStats.mMin));
+        metrics.put(resultKeyPrefix + "latency_mean", Double.toString(mLatencyStats.mMean));
+        metrics.put(resultKeyPrefix + "latency_median", Double.toString(mLatencyStats.mMedian));
+        metrics.put(resultKeyPrefix + "confidence_max", Double.toString(mConfidenceStats.mMax));
+        metrics.put(resultKeyPrefix + "confidence_min", Double.toString(mConfidenceStats.mMin));
+        metrics.put(resultKeyPrefix + "confidence_mean", Double.toString(mConfidenceStats.mMean));
+        metrics.put(
+                resultKeyPrefix + "confidence_median", Double.toString(mConfidenceStats.mMedian));
+    }
 }