Update Test to backfill Event/Gauge metric data

Bug: 195881524
Test: TH
Change-Id: Ibd9816e9de972de5a23bf89c357d740684ca51f1
Merged-In: Ibd9816e9de972de5a23bf89c357d740684ca51f1
diff --git a/common/device-side/bedstead/metricsrecorder/src/main/java/com/android/bedstead/metricsrecorder/EnterpriseMetricsRecorder.java b/common/device-side/bedstead/metricsrecorder/src/main/java/com/android/bedstead/metricsrecorder/EnterpriseMetricsRecorder.java
index 11ecfa6..671e12b 100644
--- a/common/device-side/bedstead/metricsrecorder/src/main/java/com/android/bedstead/metricsrecorder/EnterpriseMetricsRecorder.java
+++ b/common/device-side/bedstead/metricsrecorder/src/main/java/com/android/bedstead/metricsrecorder/EnterpriseMetricsRecorder.java
@@ -30,6 +30,7 @@
 import com.android.internal.os.nano.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.nano.StatsdConfigProto.StatsdConfig;
 import com.android.os.nano.AtomsProto;
+import com.android.os.nano.StatsLog;
 import com.android.os.nano.StatsLog.ConfigMetricsReportList;
 import com.android.queryable.Queryable;
 
@@ -184,7 +185,10 @@
         return Arrays.stream(reportList.reports)
                 .flatMap(s -> Arrays.stream(s.metrics.clone()))
                 .filter(s -> s.getEventMetrics() != null && s.getEventMetrics().data != null)
-                .flatMap(statsLogReport -> Arrays.stream(statsLogReport.getEventMetrics().data.clone()))
+                .flatMap(statsLogReport -> Arrays.stream(
+                        statsLogReport.getEventMetrics().data.clone()))
+                .flatMap(eventMetricData -> Arrays.stream(
+                        backfillAggregatedAtomsinEventMetric(eventMetricData)))
                 .sorted(Comparator.comparing(e -> e.elapsedTimestampNanos))
                 .map(e -> e.atom)
                 .filter((Objects::nonNull))
@@ -193,4 +197,20 @@
                 .map(EnterpriseMetricInfo::new)
                 .collect(Collectors.toList());
     }
+
+    private StatsLog.EventMetricData[] backfillAggregatedAtomsinEventMetric(
+            StatsLog.EventMetricData metricData) {
+        if (metricData.aggregatedAtomInfo == null) {
+            return new StatsLog.EventMetricData[]{metricData};
+        }
+        List<StatsLog.EventMetricData> data = new ArrayList<>();
+        StatsLog.AggregatedAtomInfo atomInfo = metricData.aggregatedAtomInfo;
+        for (long timestamp : atomInfo.elapsedTimestampNanos) {
+            StatsLog.EventMetricData newMetricData = new StatsLog.EventMetricData();
+            newMetricData.atom = atomInfo.atom;
+            newMetricData.elapsedTimestampNanos = timestamp;
+            data.add(newMetricData);
+        }
+        return data.toArray(new StatsLog.EventMetricData[0]);
+    }
 }
diff --git a/hostsidetests/appcompat/host/lib/Android.bp b/hostsidetests/appcompat/host/lib/Android.bp
index 8f44487..f6e664e 100644
--- a/hostsidetests/appcompat/host/lib/Android.bp
+++ b/hostsidetests/appcompat/host/lib/Android.bp
@@ -26,5 +26,7 @@
         "host-libprotobuf-java-full",
         "platformprotos",
     ],
-
+    static_libs: [
+        "cts-statsd-atom-host-test-utils",
+    ],
 }
diff --git a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
index 28bd4d7..9e12717 100644
--- a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
+++ b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
@@ -19,12 +19,15 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.cts.statsdatom.lib.ReportUtils;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestResult.TestStatus;
 import com.android.internal.os.StatsdConfigProto;
 import com.android.os.AtomsProto;
 import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.tradefed.build.IBuildInfo;
@@ -46,7 +49,9 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -195,19 +200,28 @@
     /**
      * Gets the statsd report. Note that this also deletes that report from statsd.
      */
-    private List<ConfigMetricsReport> getReportList(long configId) throws DeviceNotAvailableException {
+    private ConfigMetricsReportList getReportList(long configId)
+            throws DeviceNotAvailableException {
         try {
             final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
             getDevice().executeShellCommand(String.format(DUMP_REPORT_CMD, configId), receiver);
             return ConfigMetricsReportList.parser()
-                    .parseFrom(receiver.getOutput())
-                    .getReportsList();
+                    .parseFrom(receiver.getOutput());
         } catch (InvalidProtocolBufferException e) {
             throw new IllegalStateException("Failed to fetch and parse the statsd output report.",
                     e);
         }
     }
 
+    private static List<StatsLog.EventMetricData> getEventMetricDataList(
+            ConfigMetricsReportList reportList) {
+        try {
+            return ReportUtils.getEventMetricDataList(reportList);
+        } catch (Exception e) {
+            throw new IllegalStateException("Failed to parse ConfigMetrisReportList", e);
+        }
+    }
+
     /**
      * Creates and uploads a statsd config that matches the AppCompatibilityChangeReported atom
      * logged by a given package name.
@@ -313,9 +327,7 @@
     private Map<Long, Boolean> getReportedChanges(long configId, String pkgName)
             throws DeviceNotAvailableException {
         final int packageUid = getUid(pkgName);
-        return getReportList(configId).stream()
-                .flatMap(report -> report.getMetricsList().stream())
-                .flatMap(metric -> metric.getEventMetrics().getDataList().stream())
+        return getEventMetricDataList(getReportList(configId)).stream()
                 .filter(eventMetricData -> eventMetricData.hasAtom())
                 .map(eventMetricData -> eventMetricData.getAtom())
                 .map(atom -> atom.getAppCompatibilityChangeReported())
diff --git a/hostsidetests/devicepolicy/Android.bp b/hostsidetests/devicepolicy/Android.bp
index d932dd8..3432898 100644
--- a/hostsidetests/devicepolicy/Android.bp
+++ b/hostsidetests/devicepolicy/Android.bp
@@ -34,6 +34,9 @@
         "general-tests",
         "mts",
     ],
+    static_libs: [
+        "cts-statsd-atom-host-test-utils",
+    ],
     java_resource_dirs: ["res"],
     data: [":current-api-xml"],
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/Android.bp b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/Android.bp
index 7700140..dcafa40 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/Android.bp
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/Android.bp
@@ -24,4 +24,7 @@
     libs: [
         "tradefed",
     ],
+    static_libs: [
+        "cts-statsd-atom-host-test-utils",
+    ],
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java
index 5ab3ddb0..68e4934 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java
@@ -15,7 +15,7 @@
  */
 package com.android.cts.devicepolicy.metrics;
 
-import static junit.framework.Assert.assertTrue;
+import android.cts.statsdatom.lib.ReportUtils;
 
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
 import com.android.internal.os.StatsdConfigProto.EventMetric;
@@ -23,24 +23,22 @@
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
 import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.os.StatsLog.EventMetricData;
-import com.android.os.StatsLog.StatsLogReport;
 import com.android.tradefed.device.CollectingByteOutputReceiver;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
+
 import com.google.common.io.Files;
 import com.google.protobuf.InvalidProtocolBufferException;
 import com.google.protobuf.MessageLite;
 import com.google.protobuf.Parser;
+
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.List;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 /**
  * Tests Statsd atoms.
@@ -103,26 +101,7 @@
      */
     List<EventMetricData> getEventMetricDataList() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        return getEventMetricDataList(reportList);
-    }
-
-    /**
-     * Extracts and sorts the EventMetricData from the given ConfigMetricsReportList (which must
-     * contain a single report).
-     */
-    private List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
-            throws Exception {
-        assertTrue("Expected one report", reportList.getReportsCount() == 1);
-        final ConfigMetricsReport report = reportList.getReports(0);
-        final List<StatsLogReport> metricsList = report.getMetricsList();
-        return metricsList.stream()
-                .flatMap(statsLogReport -> statsLogReport.getEventMetrics().getDataList().stream())
-                .sorted(Comparator.comparing(EventMetricData::getElapsedTimestampNanos))
-                .peek(eventMetricData -> {
-                    CLog.d("Atom at " + eventMetricData.getElapsedTimestampNanos()
-                            + ":\n" + eventMetricData.getAtom().toString());
-                })
-                .collect(Collectors.toList());
+        return ReportUtils.getEventMetricDataList(reportList);
     }
 
     /** Gets the statsd report. Note that this also deletes that report from statsd. */
diff --git a/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java b/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java
index 7dc17f4..c0240dd 100644
--- a/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java
+++ b/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.cts.statsdatom.lib.ReportUtils;
 import android.stats.mediametrics_message.MediametricsMessage;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -162,12 +163,7 @@
     private MediametricsMessage.ExtractorData getMediaExtractorReportedData() throws Exception {
         ConfigMetricsReportList reportList = getAndClearReportList();
         assertThat(reportList.getReportsCount()).isEqualTo(1);
-        StatsLog.ConfigMetricsReport report = reportList.getReports(0);
-        ArrayList<StatsLog.EventMetricData> data = new ArrayList<>();
-        report.getMetricsList()
-                .forEach(
-                        statsLogReport ->
-                                data.addAll(statsLogReport.getEventMetrics().getDataList()));
+        List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(reportList);
         List<AtomsProto.MediametricsExtractorReported> mediametricsExtractorReported =
                 data.stream()
                         .map(element -> element.getAtom().getMediametricsExtractorReported())
diff --git a/hostsidetests/mediaparser/Android.bp b/hostsidetests/mediaparser/Android.bp
index 216b1c4..98e8f8f 100644
--- a/hostsidetests/mediaparser/Android.bp
+++ b/hostsidetests/mediaparser/Android.bp
@@ -36,6 +36,7 @@
     ],
     static_libs: [
         "cts-host-utils",
+        "cts-statsd-atom-host-test-utils",
     ],
     data: [
       ":CtsMediaParserTestCasesApp",
diff --git a/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java b/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java
index 1e5c856..50bfa84 100644
--- a/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java
+++ b/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.cts.statsdatom.lib.ReportUtils;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.internal.os.StatsdConfigProto;
@@ -272,16 +274,8 @@
     private List<MediametricsMediaParserReported> getMediaParserReportedEvents() throws Exception {
         ConfigMetricsReportList reportList = getAndClearReportList();
         assertThat(reportList.getReportsCount()).isEqualTo(1);
-        StatsLog.ConfigMetricsReport report = reportList.getReports(0);
-        ArrayList<EventMetricData> data = new ArrayList<>();
-        report.getMetricsList()
-                .forEach(
-                        statsLogReport ->
-                                data.addAll(statsLogReport.getEventMetrics().getDataList()));
-        // We sort the reported events by the elapsed timestamp so as to ensure they are returned
-        // in the same order as they were generated by the CTS tests.
+        List<EventMetricData> data = ReportUtils.getEventMetricDataList(reportList);
         return data.stream()
-                .sorted(Comparator.comparing(EventMetricData::getElapsedTimestampNanos))
                 .map(event -> event.getAtom().getMediametricsMediaparserReported())
                 .collect(Collectors.toList());
     }
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ReportUtils.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ReportUtils.java
index 9188894..1d9cdc2 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ReportUtils.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ReportUtils.java
@@ -19,7 +19,10 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import com.android.os.StatsLog;
+
 import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.os.StatsLog.EventMetricData;
@@ -28,12 +31,15 @@
 import com.android.os.StatsLog.StatsLogReport;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.Pair;
 
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public final class ReportUtils {
     private static final String DUMP_REPORT_CMD = "cmd stats dump-report";
@@ -51,7 +57,7 @@
 
     /**
      * Extracts and sorts the EventMetricData from the given ConfigMetricsReportList (which must
-     * contain a single report).
+     * contain a single report) and sorts the atoms by timestamp within the report.
      */
     public static List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
             throws Exception {
@@ -60,7 +66,14 @@
 
         List<EventMetricData> data = new ArrayList<>();
         for (StatsLogReport metric : report.getMetricsList()) {
-            data.addAll(metric.getEventMetrics().getDataList());
+            for (EventMetricData metricData :
+                    metric.getEventMetrics().getDataList()) {
+                if (metricData.hasAtom()) {
+                    data.add(metricData);
+                } else {
+                    data.addAll(backfillAggregatedAtomsInEventMetric(metricData));
+                }
+            }
         }
         data.sort(Comparator.comparing(EventMetricData::getElapsedTimestampNanos));
 
@@ -71,6 +84,23 @@
         return data;
     }
 
+
+    private static List<EventMetricData> backfillAggregatedAtomsInEventMetric(
+            EventMetricData metricData) {
+        if (!metricData.hasAggregatedAtomInfo()) {
+            return Collections.emptyList();
+        }
+        List<EventMetricData> data = new ArrayList<>();
+        StatsLog.AggregatedAtomInfo atomInfo = metricData.getAggregatedAtomInfo();
+        for (long timestamp : atomInfo.getElapsedTimestampNanosList()) {
+            data.add(EventMetricData.newBuilder()
+                    .setAtom(atomInfo.getAtom())
+                    .setElapsedTimestampNanos(timestamp)
+                    .build());
+        }
+        return data;
+    }
+
     public static List<Atom> getGaugeMetricAtoms(ITestDevice device) throws Exception {
         return getGaugeMetricAtoms(device, /*checkTimestampTruncated=*/false);
     }
@@ -93,9 +123,13 @@
         for (GaugeMetricData d : report.getMetrics(0).getGaugeMetrics().getDataList()) {
             assertThat(d.getBucketInfoCount()).isEqualTo(1);
             GaugeBucketInfo bucketInfo = d.getBucketInfo(0);
-            atoms.addAll(bucketInfo.getAtomList());
+            if (bucketInfo.getAtomCount() != 0) {
+                atoms.addAll(bucketInfo.getAtomList());
+            } else {
+                backFillGaugeBucketAtoms(bucketInfo.getAggregatedAtomInfoList());
+            }
             if (checkTimestampTruncated) {
-                for (long timestampNs: bucketInfo.getElapsedTimestampNanosList()) {
+                for (long timestampNs : bucketInfo.getElapsedTimestampNanosList()) {
                     assertTimestampIsTruncated(timestampNs);
                 }
             }
@@ -108,6 +142,18 @@
         return atoms;
     }
 
+    private static List<Atom> backFillGaugeBucketAtoms(
+            List<StatsLog.AggregatedAtomInfo> atomInfoList) {
+        List<Pair<Atom, Long>> atomTimestamp = new ArrayList<>();
+        for (StatsLog.AggregatedAtomInfo atomInfo : atomInfoList) {
+            for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) {
+                atomTimestamp.add(Pair.create(atomInfo.getAtom(), timestampNs));
+            }
+        }
+        atomTimestamp.sort(Comparator.comparing(o -> o.second));
+        return atomTimestamp.stream().map(p -> p.first).collect(Collectors.toList());
+    }
+
     /**
      * Delete all pre-existing reports corresponding to the CTS config.
      */