Fixes serialization of vendor metrics

Uses an updated proto model that's more efficient for serialization.

Test: Unit tests, google play and CTS tests.
Bug: 73724218

Change-Id: I936bc18216c0c67de580424b4c62344d94be6b38
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 66e4400..4991e50 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -54,17 +54,18 @@
 
     proto: {
         export_proto_headers: true,
-	type: "lite",
+        type: "lite",
     },
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
-	"libhidlbase",
+        "libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-lite",
+        "libstagefright_foundation",
         "libutils",
     ],
     cflags: [
@@ -86,24 +87,25 @@
 
     proto: {
         export_proto_headers: true,
-	type: "full",
+        type: "full",
     },
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
-	"libhidlbase",
+        "libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-full",
+        "libstagefright_foundation",
         "libutils",
     ],
     cflags: [
         // Suppress unused parameter and no error options. These cause problems
-	// when using the map type in a proto definition.
-	"-Wno-unused-parameter",
-	"-Wno-error",
+        // when using the map type in a proto definition.
+        "-Wno-unused-parameter",
+        "-Wno-error",
     ],
 }
 
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 5d97188..4e8ad52 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -32,6 +32,7 @@
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
 #include <mediadrm/DrmHal.h>
@@ -63,9 +64,27 @@
 // This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant
 // in the MediaDrm API.
 constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId";
+constexpr char kEqualsSign[] = "=";
 
+template<typename T>
+std::string toBase64StringNoPad(const T* data, size_t size) {
+    if (size == 0) {
+      return "";
+    }
+    CHECK(sizeof(data[0] == 1));
+
+    android::AString outputString;
+    encodeBase64(data, size, &outputString);
+    // Remove trailing equals padding if it exists.
+    while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) {
+        outputString.erase(outputString.size() - 1, 1);
+    }
+
+    return std::string(outputString.c_str(), outputString.size());
 }
 
+}  // anonymous namespace
+
 namespace android {
 
 #define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
@@ -101,15 +120,6 @@
     return hidl_string(string.string());
 }
 
-std::string toHexString(const std::string& str) {
-  std::ostringstream out;
-  out << std::hex << std::setfill('0');
-  for (size_t i = 0; i < str.size(); i++) {
-    out << std::setw(2) << (int)(str[i]);
-  }
-  return out.str();
-}
-
 static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) {
     switch(level) {
     case SecurityLevel::SW_SECURE_CRYPTO:
@@ -340,6 +350,7 @@
 
 sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
         const uint8_t uuid[16], const String8& appPackageName) {
+    mAppPackageName = appPackageName;
     mMetrics.SetAppPackageName(appPackageName);
 
     sp<IDrmPlugin> plugin;
@@ -1353,9 +1364,10 @@
     if (result != OK) {
         ALOGE("Failed to serialize framework metrics: %d", result);
     }
-    serializedMetrics = toHexString(serializedMetrics);
-    if (!serializedMetrics.empty()) {
-        item.setCString("serialized_metrics", serializedMetrics.c_str());
+    std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(),
+                                                        serializedMetrics.size());
+    if (!b64EncodedMetrics.empty()) {
+        item.setCString("serialized_metrics", b64EncodedMetrics.c_str());
     }
     if (!item.selfrecord()) {
         ALOGE("Failed to self record framework metrics");
@@ -1364,14 +1376,16 @@
 
 void DrmHal::reportPluginMetrics() const
 {
-    Vector<uint8_t> metrics;
+    Vector<uint8_t> metricsVector;
     String8 vendor;
     String8 description;
     if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
             getPropertyStringInternal(String8("description"), description) == OK &&
-            getPropertyByteArrayInternal(String8("metrics"), metrics) == OK) {
-        status_t res = android::reportDrmPluginMetrics(
-                metrics, vendor, description);
+            getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) {
+        std::string metricsString = toBase64StringNoPad(metricsVector.array(),
+                                                        metricsVector.size());
+        status_t res = android::reportDrmPluginMetrics(metricsString, vendor,
+                                                       description, mAppPackageName);
         if (res != OK) {
             ALOGE("Metrics were retrieved but could not be reported: %d", res);
         }
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 6c97f2b..5cb48bf 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -16,80 +16,35 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "PluginMetricsReporting"
-#include <utils/Log.h>
-#include <inttypes.h>
 
 #include <media/PluginMetricsReporting.h>
 
-#include <media/MediaAnalyticsItem.h>
+#include <inttypes.h>
 
-#include "protos/metrics.pb.h"
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Log.h>
+
 
 namespace android {
 
 namespace {
 
-using android::drm_metrics::MetricsGroup;
-using android::drm_metrics::MetricsGroup_Metric;
-using android::drm_metrics::MetricsGroup_Metric_MetricValue;
+constexpr char kSerializedMetricsField[] = "serialized_metrics";
 
-const char* const kParentAttribute = "/parent/external";
-
-status_t reportMetricsGroup(const MetricsGroup& metricsGroup,
-                            const String8& batchName,
-                            const int64_t* parentId) {
-    MediaAnalyticsItem analyticsItem(batchName.c_str());
+status_t reportVendorMetrics(const std::string& metrics,
+                             const String8& name,
+                             const String8& appPackageName) {
+    MediaAnalyticsItem analyticsItem(name.c_str());
     analyticsItem.generateSessionID();
-    int64_t sessionId = analyticsItem.getSessionID();
-    if (parentId != NULL) {
-        analyticsItem.setInt64(kParentAttribute, *parentId);
-    }
 
-    // Report the package name.
-    if (metricsGroup.has_app_package_name()) {
-        std::string app_package_name(metricsGroup.app_package_name().c_str(),
-                               metricsGroup.app_package_name().size());
-      analyticsItem.setPkgName(app_package_name);
-    }
-
-    for (int i = 0; i < metricsGroup.metric_size(); ++i) {
-        const MetricsGroup_Metric& metric = metricsGroup.metric(i);
-        if (!metric.has_name()) {
-            ALOGE("Metric with no name.");
-            return BAD_VALUE;
-        }
-
-        if (!metric.has_value()) {
-            ALOGE("Metric with no value.");
-            return BAD_VALUE;
-        }
-
-        const MetricsGroup_Metric_MetricValue& value = metric.value();
-        if (value.has_int_value()) {
-            analyticsItem.setInt64(metric.name().c_str(),
-                                   value.int_value());
-        } else if (value.has_double_value()) {
-            analyticsItem.setDouble(metric.name().c_str(),
-                                    value.double_value());
-        } else if (value.has_string_value()) {
-            analyticsItem.setCString(metric.name().c_str(),
-                                     value.string_value().c_str());
-        } else {
-            ALOGE("Metric Value with no actual value.");
-            return BAD_VALUE;
-        }
+    std::string app_package_name(appPackageName.c_str(), appPackageName.size());
+    analyticsItem.setPkgName(app_package_name);
+    if (metrics.size() > 0) {
+        analyticsItem.setCString(kSerializedMetricsField, metrics.c_str());
     }
 
     if (!analyticsItem.selfrecord()) {
-      ALOGE("selfrecord() returned false. sessioId %" PRId64, sessionId);
-    }
-
-    for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
-        const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
-        status_t res = reportMetricsGroup(subGroup, batchName, &sessionId);
-        if (res != OK) {
-            return res;
-        }
+      ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID());
     }
 
     return OK;
@@ -111,21 +66,16 @@
 
 }  // namespace
 
-status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
                                 const String8& vendor,
-                                const String8& description) {
-    MetricsGroup root_metrics_group;
-    if (!root_metrics_group.ParseFromArray(serializedMetrics.array(),
-                                           serializedMetrics.size())) {
-        ALOGE("Failure to parse.");
-        return BAD_VALUE;
-    }
+                                const String8& description,
+                                const String8& appPackageName) {
 
     String8 name = String8::format("drm.vendor.%s.%s",
                                    sanitize(vendor).c_str(),
                                    sanitize(description).c_str());
 
-    return reportMetricsGroup(root_metrics_group, name, NULL);
+    return reportVendorMetrics(b64EncodedMetrics, name, appPackageName);
 }
 
 }  // namespace android
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
index aa26f5f..6160e6f 100644
--- a/drm/libmediadrm/protos/metrics.proto
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -18,33 +18,6 @@
 
 package android.drm_metrics;
 
-// The MetricsGroup is a collection of metric name/value pair instances
-// that can be serialized and provided to a caller.
-message MetricsGroup {
-  message Metric {
-    message MetricValue {
-      // Exactly one of the following values must be set.
-      optional int64 int_value = 1;
-      optional double double_value = 2;
-      optional string string_value = 3;
-    }
-
-    // The name of the metric. Must be valid UTF-8. Required.
-    optional string name = 1;
-
-    // The value of the metric. Required.
-    optional MetricValue value = 2;
-  }
-
-  // The list of name/value pairs of metrics.
-  repeated Metric metric = 1;
-
-  // Allow multiple sub groups of metrics.
-  repeated MetricsGroup metric_sub_group = 2;
-
-  // Name of the application package associated with the metrics.
-  optional string app_package_name = 3;
-}
 
 // This message contains the specific metrics captured by DrmMetrics. It is
 // used for serializing and logging metrics.
@@ -72,7 +45,7 @@
   // The Counter message is used to store a count value with an associated
   // Attribute.
   message Counter {
-    optional int64 count = 1;
+    optional uint64 count = 1;
     // Represents the attributes associated with this counter instance.
     optional Attributes attributes = 2;
   }
@@ -80,11 +53,11 @@
   // The DistributionMetric is meant to capture the moments of a normally
   // distributed (or approximately normal) value.
   message DistributionMetric {
-    optional double min = 1;
-    optional double max = 2;
-    optional double mean = 3;
+    optional float min = 1;
+    optional float max = 2;
+    optional float mean = 3;
     optional double variance = 4;
-    optional double operation_count = 5;
+    optional uint64 operation_count = 5;
 
     // Represents the attributes assocated with this distribution metric
     // instance.
@@ -93,9 +66,9 @@
 
   message SessionLifetime {
     // Start time of the session in milliseconds since epoch.
-    optional int64 start_time_ms = 1;
+    optional uint64 start_time_ms = 1;
     // End time of the session in milliseconds since epoch.
-    optional int64 end_time_ms = 2;
+    optional uint64 end_time_ms = 2;
   }
 
   // The count of open session operations. Each instance has a specific error
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index b0e1cc8..f267f76 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -182,6 +182,7 @@
     const Vector<sp<IDrmFactory>> mFactories;
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
+    String8 mAppPackageName;
 
     // Mutable to allow modification within GetPropertyByteArray.
     mutable MediaDrmMetrics mMetrics;
diff --git a/media/libmedia/include/media/PluginMetricsReporting.h b/media/libmedia/include/media/PluginMetricsReporting.h
index 4a5a363..e00bd43 100644
--- a/media/libmedia/include/media/PluginMetricsReporting.h
+++ b/media/libmedia/include/media/PluginMetricsReporting.h
@@ -20,13 +20,13 @@
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
-#include <utils/Vector.h>
 
 namespace android {
 
-status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
                                 const String8& vendorName,
-                                const String8& description);
+                                const String8& description,
+                                const String8& appPackageName);
 
 }  // namespace android