Add logging support for FW metrics

Adds proto serialization and logging support for media drm framework metrics.

Bug: 64001676

Test: Ran CTS tests, unit tests and Google Play.
Change-Id: Ie350ac93caa6b35610eb63e4acc860c8e8a5cf5b
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index ea239c5..5ea4614 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -15,25 +15,20 @@
         "IDrm.cpp",
         "IDrmClient.cpp",
         "IMediaDrmService.cpp",
-        "PluginMetricsReporting.cpp",
         "SharedLibrary.cpp",
         "DrmHal.cpp",
-	"DrmMetrics.cpp",
         "CryptoHal.cpp",
-        "protos/plugin_metrics.proto",
     ],
 
-    proto: {
-        type: "lite",
-    },
-
     shared_libs: [
         "libbinder",
         "libcutils",
         "libdl",
         "liblog",
+        "libmediadrmmetrics_lite",
         "libmediametrics",
         "libmediautils",
+        "libprotobuf-cpp-lite",
         "libstagefright_foundation",
         "libutils",
         "android.hardware.drm@1.0",
@@ -48,3 +43,66 @@
         "-Wall",
     ],
 }
+
+// This is the version of the drm metrics configured for protobuf lite.
+cc_library_shared {
+    name: "libmediadrmmetrics_lite",
+    srcs: [
+        "DrmMetrics.cpp",
+        "PluginMetricsReporting.cpp",
+        "protos/metrics.proto",
+    ],
+
+    proto: {
+        export_proto_headers: true,
+	type: "lite",
+    },
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libmediametrics",
+        "libprotobuf-cpp-lite",
+        "libutils",
+    ],
+    cflags: [
+        // Suppress unused parameter and no error options. These cause problems
+        // with the when using the map type in a proto definition.
+        "-Wno-unused-parameter",
+        "-Wno-error",
+    ],
+}
+
+// This is the version of the drm metrics library configured for full protobuf.
+cc_library_shared {
+    name: "libmediadrmmetrics_full",
+    srcs: [
+        "DrmMetrics.cpp",
+        "PluginMetricsReporting.cpp",
+        "protos/metrics.proto",
+    ],
+
+    proto: {
+        export_proto_headers: true,
+	type: "full",
+    },
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libmediametrics",
+        "libprotobuf-cpp-full",
+        "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",
+    ],
+}
+
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 6b782f0..48f4479 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,6 +16,8 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "DrmHal"
+#include <iomanip>
+
 #include <utils/Log.h>
 
 #include <binder/IPCThreadState.h>
@@ -98,6 +100,15 @@
     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:
@@ -309,6 +320,7 @@
 
 sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
         const uint8_t uuid[16], const String8& appPackageName) {
+    mMetrics.SetAppPackageName(appPackageName);
 
     sp<IDrmPlugin> plugin;
     Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
@@ -504,7 +516,8 @@
     INIT_CHECK();
 
     closeOpenSessions();
-    reportMetrics();
+    reportPluginMetrics();
+    reportFrameworkMetrics();
     setListener(NULL);
     mInitCheck = NO_INIT;
 
@@ -620,7 +633,7 @@
         status_t response = toStatusT(status);
         mMetrics.SetSessionEnd(sessionId);
         mMetrics.mCloseSessionCounter.Increment(response);
-        reportMetrics();
+        reportPluginMetrics();
         return response;
     }
     mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
@@ -634,7 +647,7 @@
         String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
-    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
+    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -729,7 +742,7 @@
 status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
         Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
     Mutex::Autolock autoLock(mLock);
-    EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTiming);
+    EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
 
     INIT_CHECK();
 
@@ -1277,8 +1290,41 @@
     }
 }
 
+void DrmHal::reportFrameworkMetrics() const
+{
+    MediaAnalyticsItem item("mediadrm");
+    item.generateSessionID();
+    item.setPkgName(mMetrics.GetAppPackageName().c_str());
+    String8 vendor;
+    String8 description;
+    status_t result = getPropertyStringInternal(String8("vendor"), vendor);
+    if (result != OK) {
+        ALOGE("Failed to get vendor from drm plugin. %d", result);
+    } else {
+        item.setCString("vendor", vendor.c_str());
+    }
+    result = getPropertyStringInternal(String8("description"), description);
+    if (result != OK) {
+        ALOGE("Failed to get description from drm plugin. %d", result);
+    } else {
+        item.setCString("description", description.c_str());
+    }
 
-void DrmHal::reportMetrics() const
+    std::string serializedMetrics;
+    result = mMetrics.GetSerializedMetrics(&serializedMetrics);
+    if (result != OK) {
+        ALOGE("Failed to serialize Framework metrics: %d", result);
+    }
+    serializedMetrics = ToHexString(serializedMetrics);
+    if (!serializedMetrics.empty()) {
+        item.setCString("serialized_metrics", serializedMetrics.c_str());
+    }
+    if (!item.selfrecord()) {
+        ALOGE("Failed to self record framework metrics.");
+    }
+}
+
+void DrmHal::reportPluginMetrics() const
 {
     Vector<uint8_t> metrics;
     String8 vendor;
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index dd91058..03bd88a 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -13,15 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#define LOG_TAG "DrmMetrics"
+#include <iomanip>
 #include <utility>
 
 #include <android-base/macros.h>
 #include <media/DrmMetrics.h>
 #include <media/stagefright/foundation/base64.h>
 #include <sys/time.h>
+#include <utils/Log.h>
 #include <utils/Timers.h>
 
+#include "protos/metrics.pb.h"
+
+using ::android::String16;
+using ::android::String8;
+using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
 using ::android::os::PersistableBundle;
@@ -130,8 +137,7 @@
 }
 
 void ExportSessionLifespans(
-    const std::map<android::String16, std::pair<int64_t, int64_t>>
-        &mSessionLifespans,
+    const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans,
     PersistableBundle *metrics) {
     if (!metrics) {
         ALOGE("metrics was unexpectedly null.");
@@ -146,9 +152,9 @@
     PersistableBundle endTimesBundle;
     for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end();
          it++) {
-        startTimesBundle.putLong(android::String16(it->first),
-                                 it->second.first);
-        endTimesBundle.putLong(android::String16(it->first), it->second.second);
+        String16 key(it->first.c_str(), it->first.size());
+        startTimesBundle.putLong(key, it->second.first);
+        endTimesBundle.putLong(key, it->second.second);
     }
     metrics->putPersistableBundle(
         android::String16("drm.mediadrm.session_start_times_ms"),
@@ -157,18 +163,13 @@
         android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
 }
 
-android::String8 ToHexString(const android::Vector<uint8_t> &sessionId) {
-    android::String8 result;
+std::string ToHexString(const android::Vector<uint8_t> &sessionId) {
+    std::ostringstream out;
+    out << std::hex << std::setfill('0');
     for (size_t i = 0; i < sessionId.size(); i++) {
-        result.appendFormat("%02x", sessionId[i]);
+        out << std::setw(2) << (int)(sessionId[i]);
     }
-    return result;
-}
-
-int64_t getCurrentTimeMs() {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
+    return out.str();
 }
 
 } // namespace
@@ -178,8 +179,8 @@
 MediaDrmMetrics::MediaDrmMetrics()
     : mOpenSessionCounter("drm.mediadrm.open_session", "status"),
       mCloseSessionCounter("drm.mediadrm.close_session", "status"),
-      mGetKeyRequestTiming("drm.mediadrm.get_key_request", "status"),
-      mProvideKeyResponseTiming("drm.mediadrm.provide_key_response", "status"),
+      mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"),
+      mProvideKeyResponseTimeUs("drm.mediadrm.provide_key_response", "status"),
       mGetProvisionRequestCounter("drm.mediadrm.get_provision_request",
                                   "status"),
       mProvideProvisionResponseCounter(
@@ -192,14 +193,14 @@
 
 void MediaDrmMetrics::SetSessionStart(
     const android::Vector<uint8_t> &sessionId) {
-    String16 sessionIdHex = String16(ToHexString(sessionId));
+    std::string sessionIdHex = ToHexString(sessionId);
     mSessionLifespans[sessionIdHex] =
-        std::make_pair(getCurrentTimeMs(), (int64_t)0);
+        std::make_pair(GetCurrentTimeMs(), (int64_t)0);
 }
 
 void MediaDrmMetrics::SetSessionEnd(const android::Vector<uint8_t> &sessionId) {
-    String16 sessionIdHex = String16(ToHexString(sessionId));
-    int64_t endTimeMs = getCurrentTimeMs();
+    std::string sessionIdHex = ToHexString(sessionId);
+    int64_t endTimeMs = GetCurrentTimeMs();
     if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) {
         mSessionLifespans[sessionIdHex] =
             std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs);
@@ -215,8 +216,8 @@
     }
     ExportCounterMetric(mOpenSessionCounter, metrics);
     ExportCounterMetric(mCloseSessionCounter, metrics);
-    ExportEventMetric(mGetKeyRequestTiming, metrics);
-    ExportEventMetric(mProvideKeyResponseTiming, metrics);
+    ExportEventMetric(mGetKeyRequestTimeUs, metrics);
+    ExportEventMetric(mProvideKeyResponseTimeUs, metrics);
     ExportCounterMetric(mGetProvisionRequestCounter, metrics);
     ExportCounterMetric(mProvideProvisionResponseCounter, metrics);
     ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics);
@@ -225,4 +226,117 @@
     ExportSessionLifespans(mSessionLifespans, metrics);
 }
 
+status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) {
+
+    if (!serializedMetrics) {
+        ALOGE("serializedMetrics was unexpectedly null.");
+        return UNEXPECTED_NULL;
+    }
+
+    DrmFrameworkMetrics metrics;
+
+    mOpenSessionCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_open_session_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mCloseSessionCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_close_session_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mGetProvisionRequestCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_get_provisioning_request_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mProvideProvisionResponseCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_provide_provisioning_response_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mKeyStatusChangeCounter.ExportValues(
+        [&](const KeyStatusType key_status_type, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_key_status_change_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_key_status_type(
+                (uint32_t)key_status_type);
+        });
+
+    mEventCounter.ExportValues(
+        [&](const EventType event_type, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_event_callback_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_event_type((uint32_t)event_type);
+        });
+
+    mGetDeviceUniqueIdCounter.ExportValues(
+        [&](const status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_get_device_unique_id_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mGetKeyRequestTimeUs.ExportValues(
+        [&](const status_t status, const EventStatistics &stats) {
+            DrmFrameworkMetrics::DistributionMetric *metric =
+                metrics.add_get_key_request_time_us();
+            metric->set_min(stats.min);
+            metric->set_max(stats.max);
+            metric->set_mean(stats.mean);
+            metric->set_operation_count(stats.count);
+            metric->set_variance(stats.sum_squared_deviation / stats.count);
+            metric->mutable_attributes()->set_error_code(status);
+        });
+
+    mProvideKeyResponseTimeUs.ExportValues(
+        [&](const status_t status, const EventStatistics &stats) {
+            DrmFrameworkMetrics::DistributionMetric *metric =
+                metrics.add_provide_key_response_time_us();
+            metric->set_min(stats.min);
+            metric->set_max(stats.max);
+            metric->set_mean(stats.mean);
+            metric->set_operation_count(stats.count);
+            metric->set_variance(stats.sum_squared_deviation / stats.count);
+            metric->mutable_attributes()->set_error_code(status);
+        });
+
+    for (const auto &sessionLifespan : mSessionLifespans) {
+        auto *map = metrics.mutable_session_lifetimes();
+
+        (*map)[sessionLifespan.first].set_start_time_ms(
+            sessionLifespan.second.first);
+        (*map)[sessionLifespan.first].set_end_time_ms(
+            sessionLifespan.second.second);
+    }
+
+    if (!metrics.SerializeToString(serializedMetrics)) {
+        ALOGE("Failed to serialize metrics.");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+int64_t MediaDrmMetrics::GetCurrentTimeMs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
+}
+
 } // namespace android
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 26c8427..6c97f2b 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -23,7 +23,7 @@
 
 #include <media/MediaAnalyticsItem.h>
 
-#include "protos/plugin_metrics.pb.h"
+#include "protos/metrics.pb.h"
 
 namespace android {
 
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
new file mode 100644
index 0000000..aa26f5f
--- /dev/null
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+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.
+// next id: 11.
+message DrmFrameworkMetrics {
+  // TODO: Consider using extensions.
+
+  // Attributes are associated with a recorded value. E.g. A counter may
+  // represent a count of an operation returning a specific error code. The
+  // error code will be an attribute.
+  message Attributes {
+    // Reserved for compatibility with logging proto.
+    reserved 2 to 13;
+
+    // A general purpose error code where 0 means OK.
+    optional int32 error_code = 1;
+
+    // Defined at ::android::hardware::drm::V1_0::KeyStatusType;
+    optional uint32 key_status_type = 14;
+
+    // Defined at ::android::hardware::drm::V1_0::EventType;
+    optional uint32 event_type = 15;
+  }
+
+  // The Counter message is used to store a count value with an associated
+  // Attribute.
+  message Counter {
+    optional int64 count = 1;
+    // Represents the attributes associated with this counter instance.
+    optional Attributes attributes = 2;
+  }
+
+  // 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 double variance = 4;
+    optional double operation_count = 5;
+
+    // Represents the attributes assocated with this distribution metric
+    // instance.
+    optional Attributes attributes = 6;
+  }
+
+  message SessionLifetime {
+    // Start time of the session in milliseconds since epoch.
+    optional int64 start_time_ms = 1;
+    // End time of the session in milliseconds since epoch.
+    optional int64 end_time_ms = 2;
+  }
+
+  // The count of open session operations. Each instance has a specific error
+  // code associated with it.
+  repeated Counter open_session_counter = 1;
+
+  // The count of close session operations. Each instance has a specific error
+  // code associated with it.
+  repeated Counter close_session_counter = 2;
+
+  // Count and execution time of getKeyRequest calls.
+  repeated DistributionMetric get_key_request_time_us = 3;
+
+  // Count and execution time of provideKeyResponse calls.
+  repeated DistributionMetric provide_key_response_time_us = 4;
+
+  // Count of getProvisionRequest calls.
+  repeated Counter get_provisioning_request_counter = 5;
+
+  // Count of provideProvisionResponse calls.
+  repeated Counter provide_provisioning_response_counter = 6;
+
+  // Count of key status events broken out by status type.
+  repeated Counter key_status_change_counter = 7;
+
+  // Count of events broken out by event type
+  repeated Counter event_callback_counter = 8;
+
+  // Count getPropertyByteArray calls to retrieve the device unique id.
+  repeated Counter get_device_unique_id_counter = 9;
+
+  // Session ids to lifetime (start and end time) map.
+  // Session ids are strings of hex-encoded byte arrays.
+  map<string, SessionLifetime> session_lifetimes = 10;
+}
+
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
deleted file mode 100644
index 7e3bcf5..0000000
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.drm_metrics;
-
-// need this if we are using libprotobuf-cpp-2.3.0-lite
-option optimize_for = LITE_RUNTIME;
-
-// 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;
-}
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index b508c59..670d3b9 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -18,15 +18,21 @@
       "android.hardware.drm@1.0",
       "libbinder",
       "liblog",
-      "libmediadrm",
+      "libmediadrmmetrics_full",
       "libmediametrics",
+      "libprotobuf-cpp-full",
       "libutils",
     ],
-    include_dirs: ["frameworks/av/include/media"],
-    cflags: [
-      "-Werror",
-      "-Wall",
+    static_libs: ["libgmock"],
+    include_dirs: [
+      "frameworks/av/include/media",
     ],
+    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",
+    ]
 }
 
 cc_test {
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index d774eae..fe762c9 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -14,21 +14,40 @@
  * limitations under the License.
  */
 
-#include <binder/PersistableBundle.h>
-#include <gtest/gtest.h>
-
+#define LOG_TAG "DrmMetricsTest"
 #include "DrmMetrics.h"
 
+#include <binder/PersistableBundle.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "protos/metrics.pb.h"
+
+using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
 using ::android::os::PersistableBundle;
+using ::google::protobuf::util::MessageDifferencer;
+using ::google::protobuf::TextFormat;
 
 namespace android {
 
 /**
  * Unit tests for the MediaDrmMetrics class.
  */
-class MediaDrmMetricsTest : public ::testing::Test {
+class MediaDrmMetricsTest : public ::testing::Test {};
+
+/**
+ * This derived class mocks the clock for testing purposes.
+ */
+class FakeMediaDrmMetrics : public MediaDrmMetrics {
+ public:
+  FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {};
+
+  int64_t GetCurrentTimeMs() { return time_++; }
+  int64_t time_;
 };
 
 TEST_F(MediaDrmMetricsTest, EmptySuccess) {
@@ -46,9 +65,9 @@
   metrics.mCloseSessionCounter.Increment(OK);
 
   {
-    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
     EventTimer<status_t> provide_key_response_timer(
-        &metrics.mProvideKeyResponseTiming);
+        &metrics.mProvideKeyResponseTimeUs);
     get_key_request_timer.SetAttribute(OK);
     provide_key_response_timer.SetAttribute(OK);
   }
@@ -109,9 +128,9 @@
 
   for (status_t s : {OK, UNEXPECTED_NULL}) {
     {
-      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
       EventTimer<status_t> provide_key_response_timer(
-          &metrics.mProvideKeyResponseTiming);
+          &metrics.mProvideKeyResponseTimeUs);
       get_key_request_timer.SetAttribute(s);
       provide_key_response_timer.SetAttribute(s);
     }
@@ -135,9 +154,23 @@
   metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
   metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
 
+  android::Vector<uint8_t> sessionId1;
+  sessionId1.push_back(1);
+  sessionId1.push_back(2);
+  android::Vector<uint8_t> sessionId2;
+  sessionId2.push_back(3);
+  sessionId2.push_back(4);
+  String16 hexSessionId1("0102");
+  String16 hexSessionId2("0304");
+
+  metrics.SetSessionStart(sessionId1);
+  metrics.SetSessionStart(sessionId2);
+  metrics.SetSessionEnd(sessionId2);
+  metrics.SetSessionEnd(sessionId1);
+
   PersistableBundle bundle;
   metrics.Export(&bundle);
-  EXPECT_EQ(33U, bundle.size());
+  EXPECT_EQ(35U, bundle.size());
 
   // Verify the list of pairs of int64 metrics.
   std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -194,6 +227,25 @@
     }
   }
 
+  // Verify the lifespans
+  PersistableBundle start_times;
+  PersistableBundle end_times;
+  String16 start_time_key("drm.mediadrm.session_start_times_ms");
+  String16 end_time_key("drm.mediadrm.session_end_times_ms");
+  ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times));
+  ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times));
+  EXPECT_EQ(2U, start_times.size());
+  EXPECT_EQ(2U, end_times.size());
+  int64_t start_time, end_time;
+  for (const auto& sid : { hexSessionId1, hexSessionId2 }) {
+    start_time = -1;
+    end_time = -1;
+    EXPECT_TRUE(start_times.getLong(sid, &start_time));
+    EXPECT_TRUE(end_times.getLong(sid, &end_time));
+    EXPECT_GT(start_time, 0);
+    EXPECT_GE(end_time, start_time);
+  }
+
   // Validate timing values exist.
   String16 get_key_request_key(
       "drm.mediadrm.get_key_request.ok.average_time_micros");
@@ -207,4 +259,157 @@
   EXPECT_GE(value, 0);
 }
 
+
+TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+  metrics.mCloseSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+  metrics.mGetProvisionRequestCounter.Increment(OK);
+  metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
+  metrics.mProvideProvisionResponseCounter.Increment(OK);
+  metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
+  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+  metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
+
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
+  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+  metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
+  metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
+  metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
+  metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "open_session_counter { count: 1 attributes { error_code: 0 } } "
+      "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "close_session_counter { count: 1 attributes { error_code: 0 } } "
+      "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } "
+      "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+      "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } "
+      "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+      "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 0 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 1 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 2 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 3 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 4 } } ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) {
+  MediaDrmMetrics metrics;
+
+  for (status_t s : {OK, UNEXPECTED_NULL}) {
+    double time = 0;
+    for (int i = 0; i < 5; i++) {
+      time += 1.0;
+      metrics.mGetKeyRequestTimeUs.Record(time, s);
+      metrics.mProvideKeyResponseTimeUs.Record(time, s);
+    }
+  }
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "get_key_request_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: -0x7FFFFFF8 } "
+      "} "
+      "get_key_request_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: 0 } "
+      "} "
+      "provide_key_response_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: -0x7FFFFFF8 } "
+      "} "
+      "provide_key_response_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: 0 } "
+      "} ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) {
+  // Use the fake so the clock is predictable;
+  FakeMediaDrmMetrics metrics;
+
+  android::Vector<uint8_t> sessionId1;
+  sessionId1.push_back(1);
+  sessionId1.push_back(2);
+  android::Vector<uint8_t> sessionId2;
+  sessionId2.push_back(3);
+  sessionId2.push_back(4);
+
+  metrics.SetSessionStart(sessionId1);
+  metrics.SetSessionStart(sessionId2);
+  metrics.SetSessionEnd(sessionId2);
+  metrics.SetSessionEnd(sessionId1);
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "session_lifetimes: { "
+      "  key: '0102' "
+      "  value { start_time_ms: 0 end_time_ms: 3 } "
+      "} "
+      "session_lifetimes: { "
+      "  key: '0304' "
+      "  value { start_time_ms: 1 end_time_ms: 2 } "
+      "} ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
 }  // namespace android
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 67659b2..bf91ea9 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -203,7 +203,8 @@
 
     void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
 
-    void reportMetrics() const;
+    void reportPluginMetrics() const;
+    void reportFrameworkMetrics() const;
     status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
     status_t getPropertyByteArrayInternal(String8 const &name,
                                           Vector<uint8_t> &value) const;
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index d6d6ae6..5c2fdf2 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -34,14 +34,15 @@
 class MediaDrmMetrics {
  public:
   explicit MediaDrmMetrics();
+  virtual ~MediaDrmMetrics() {};
   // Count of openSession calls.
   CounterMetric<status_t> mOpenSessionCounter;
   // Count of closeSession calls.
   CounterMetric<status_t> mCloseSessionCounter;
   // Count and timing of getKeyRequest calls.
-  EventMetric<status_t> mGetKeyRequestTiming;
+  EventMetric<status_t> mGetKeyRequestTimeUs;
   // Count and timing of provideKeyResponse calls.
-  EventMetric<status_t> mProvideKeyResponseTiming;
+  EventMetric<status_t> mProvideKeyResponseTimeUs;
   // Count of getProvisionRequest calls.
   CounterMetric<status_t> mGetProvisionRequestCounter;
   // Count of provideProvisionResponse calls.
@@ -62,13 +63,31 @@
   // Adds a session end time record.
   void SetSessionEnd(const Vector<uint8_t>& sessionId);
 
+  // The app package name is the application package name that is using the
+  // instance. The app package name is held here for convenience. It is not
+  // serialized or exported with the metrics.
+  void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; }
+  const String8& GetAppPackageName() { return mAppPackageName; }
+
   // Export the metrics to a PersistableBundle.
   void Export(os::PersistableBundle* metricsBundle);
 
+  // Get the serialized metrics. Metrics are formatted as a serialized
+  // DrmFrameworkMetrics proto. If there is a failure serializing the metrics,
+  // this returns an error. The parameter |serlializedMetrics| is owned by the
+  // caller and must not be null.
+  status_t GetSerializedMetrics(std::string* serializedMetrics);
+
+ protected:
+  // This is visible for testing only.
+  virtual int64_t GetCurrentTimeMs();
+
  private:
   // Session lifetimes. A pair of values representing the milliseconds since
   // epoch, UTC. The first value is the start time, the second is the end time.
-  std::map<String16, std::pair<int64_t, int64_t>> mSessionLifespans;
+  std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
+
+  String8 mAppPackageName;
 };
 
 }  // namespace android