Adds most remaining framework metrics

Adds nearly all of the remaining specified framework level metrics.
Also adds a basic unit test to verify that metrics are exported
correctly. A follow-up CL will update the CTS test to touch all metric
values in DrmHal.

BUG: 64001676
Test: New unit test. Smoke tested with GPlay/Walleye.
Change-Id: I4df90675ae304d3c62b7886537328b3d848fd77c
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index ff7ed58..f2e9df7 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -54,6 +54,14 @@
 using ::android::hidl::manager::V1_0::IServiceManager;
 using ::android::sp;
 
+namespace {
+
+// This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant
+// in the MediaDrm API.
+constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId";
+
+}
+
 namespace android {
 
 #define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
@@ -320,6 +328,7 @@
 
 Return<void> DrmHal::sendEvent(EventType hEventType,
         const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) {
+    mMetrics.mEventCounter.Increment(hEventType);
 
     mEventLock.lock();
     sp<IDrmClient> listener = mListener;
@@ -410,12 +419,21 @@
                 break;
             }
             obj.writeInt32(type);
+            mMetrics.mKeyStatusChangeCounter.Increment(keyStatus.type);
         }
         obj.writeInt32(hasNewUsableKey);
 
         Mutex::Autolock lock(mNotifyLock);
         listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
+    } else {
+        // There's no listener. But we still want to count the key change
+        // events.
+        size_t nKeys = keyStatusList.size();
+        for (size_t i = 0; i < nKeys; i++) {
+            mMetrics.mKeyStatusChangeCounter.Increment(keyStatusList[i].type);
+        }
     }
+
     return Void();
 }
 
@@ -539,8 +557,11 @@
             }
         }
         reportMetrics();
-        return toStatusT(status);
+        status_t response = toStatusT(status);
+        mMetrics.mCloseSessionCounter.Increment(response);
+        return response;
     }
+    mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
     return DEAD_OBJECT;
 }
 
@@ -646,6 +667,8 @@
 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);
+
     INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
@@ -661,8 +684,9 @@
                 err = toStatusT(status);
             }
         );
-
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    keyResponseTimer.SetAttribute(err);
+    return err;
 }
 
 status_t DrmHal::removeKeys(Vector<uint8_t> const &keySetId) {
@@ -726,7 +750,9 @@
             }
         );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mGetProvisionRequestCounter.Increment(err);
+    return err;
 }
 
 status_t DrmHal::provideProvisionResponse(Vector<uint8_t> const &response,
@@ -747,7 +773,9 @@
             }
         );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mProvideProvisionResponseCounter.Increment(err);
+    return err;
 }
 
 status_t DrmHal::getSecureStops(List<Vector<uint8_t>> &secureStops) {
@@ -969,7 +997,11 @@
             }
     );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    if (name == kPropertyDeviceUniqueId) {
+        mMetrics.mGetDeviceUniqueIdCounter.Increment(err);
+    }
+    return err;
 }
 
 status_t DrmHal::setPropertyString(String8 const &name, String8 const &value ) const {
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index f536545..68730b7 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -14,13 +14,46 @@
  * limitations under the License.
  */
 
+#include <android-base/macros.h>
 #include <media/DrmMetrics.h>
 
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyStatusType;
+
 namespace {
 
 template<typename T>
+std::string GetAttributeName(T type);
+
+template<>
+std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
+  static const char* type_names[] = {
+      "USABLE", "EXPIRED", "OUTPUT_NOT_ALLOWED",
+      "STATUS_PENDING", "INTERNAL_ERROR" };
+  if (((size_t) type) > arraysize(type_names)) {
+    return "UNKNOWN_TYPE";
+  }
+  return type_names[(size_t) type];
+}
+
+template<>
+std::string GetAttributeName<EventType>(EventType type) {
+  static const char* type_names[] = {
+      "PROVISION_REQUIRED", "KEY_NEEDED", "KEY_EXPIRED",
+      "VENDOR_DEFINED", "SESSION_RECLAIMED" };
+  if (((size_t) type) > arraysize(type_names)) {
+    return "UNKNOWN_TYPE";
+  }
+  return type_names[(size_t) type];
+}
+
+template<typename T>
 void ExportCounterMetric(const android::CounterMetric<T>& counter,
                          android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
   std::string success_count_name = counter.metric_name() + "/ok/count";
   std::string error_count_name = counter.metric_name() + "/error/count";
   counter.ExportValues(
@@ -38,11 +71,31 @@
 }
 
 template<typename T>
+void ExportCounterMetricWithAttributeNames(
+    const android::CounterMetric<T>& counter,
+    android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
+  counter.ExportValues(
+      [&] (const T& attribute, const int64_t value) {
+          std::string name = counter.metric_name()
+              + "/" + GetAttributeName(attribute) + "/count";
+          item->setInt64(name.c_str(), value);
+      });
+}
+
+template<typename T>
 void ExportEventMetric(const android::EventMetric<T>& event,
                        android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
   std::string success_count_name = event.metric_name() + "/ok/count";
   std::string error_count_name = event.metric_name() + "/error/count";
-  std::string timing_name = event.metric_name() + "/average_time_micros";
+  std::string timing_name = event.metric_name() + "/ok/average_time_micros";
   event.ExportValues(
       [&] (const android::status_t& status,
            const android::EventStatistics& value) {
@@ -66,12 +119,34 @@
 
 MediaDrmMetrics::MediaDrmMetrics()
     : mOpenSessionCounter("/drm/mediadrm/open_session", "status"),
-      mGetKeyRequestTiming("/drm/mediadrm/get_key_request", "status") {
+      mCloseSessionCounter("/drm/mediadrm/close_session", "status"),
+      mGetKeyRequestTiming("/drm/mediadrm/get_key_request", "status"),
+      mProvideKeyResponseTiming("/drm/mediadrm/provide_key_response", "status"),
+      mGetProvisionRequestCounter(
+          "/drm/mediadrm/get_provision_request", "status"),
+      mProvideProvisionResponseCounter(
+          "/drm/mediadrm/provide_provision_response", "status"),
+      mKeyStatusChangeCounter(
+          "/drm/mediadrm/key_status_change", "key_status_type"),
+      mEventCounter("/drm/mediadrm/event", "event_type"),
+      mGetDeviceUniqueIdCounter(
+          "/drm/mediadrm/get_device_unique_id", "status") {
 }
 
 void MediaDrmMetrics::Export(MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
   ExportCounterMetric(mOpenSessionCounter, item);
+  ExportCounterMetric(mCloseSessionCounter, item);
   ExportEventMetric(mGetKeyRequestTiming, item);
+  ExportEventMetric(mProvideKeyResponseTiming, item);
+  ExportCounterMetric(mGetProvisionRequestCounter, item);
+  ExportCounterMetric(mProvideProvisionResponseCounter, item);
+  ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, item);
+  ExportCounterMetricWithAttributeNames(mEventCounter, item);
+  ExportCounterMetric(mGetDeviceUniqueIdCounter, item);
 }
 
 }  // namespace android
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 485e78c..fdc982d 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -12,6 +12,23 @@
 }
 
 cc_test {
+    name: "DrmMetrics_test",
+    srcs: ["DrmMetrics_test.cpp"],
+    shared_libs: [
+      "android.hardware.drm@1.0",
+      "liblog",
+      "libmediadrm",
+      "libmediametrics",
+      "libutils",
+    ],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
+
+cc_test {
     name: "EventMetric_test",
     srcs: ["EventMetric_test.cpp"],
     shared_libs: [
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
new file mode 100644
index 0000000..87e9752
--- /dev/null
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "DrmMetrics.h"
+
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyStatusType;
+
+namespace android {
+
+/**
+ * Unit tests for the MediaDrmMetrics class.
+ */
+class MediaDrmMetricsTest : public ::testing::Test {
+};
+
+TEST_F(MediaDrmMetricsTest, EmptySuccess) {
+  MediaDrmMetrics metrics;
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(0, item.count());
+}
+
+TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(OK);
+
+  {
+    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+    EventTimer<status_t> provide_key_response_timer(
+        &metrics.mProvideKeyResponseTiming);
+    get_key_request_timer.SetAttribute(OK);
+    provide_key_response_timer.SetAttribute(OK);
+  }
+
+  metrics.mGetProvisionRequestCounter.Increment(OK);
+  metrics.mProvideProvisionResponseCounter.Increment(OK);
+  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(11, item.count());
+
+  // Verify the list of pairs of int64 metrics.
+  std::vector<std::pair<std::string, int64_t>> expected_values = {
+      { "/drm/mediadrm/open_session/ok/count", 1 },
+      { "/drm/mediadrm/close_session/ok/count", 1 },
+      { "/drm/mediadrm/get_key_request/ok/count", 1 },
+      { "/drm/mediadrm/provide_key_response/ok/count", 1 },
+      { "/drm/mediadrm/get_provision_request/ok/count", 1 },
+      { "/drm/mediadrm/provide_provision_response/ok/count", 1 },
+      { "/drm/mediadrm/key_status_change/USABLE/count", 1 },
+      { "/drm/mediadrm/event/PROVISION_REQUIRED/count", 1 },
+      { "/drm/mediadrm/get_device_unique_id/ok/count", 1 }};
+  for (const auto& expected_pair : expected_values) {
+    int64_t value = -1;
+    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
+        << "Failed to get " << expected_pair.first;
+    EXPECT_EQ(expected_pair.second, value)
+        << "Unexpected value for " << expected_pair.first;
+  }
+
+  // Validate timing values exist.
+  int64_t value = -1;
+  EXPECT_TRUE(
+      item.getInt64("/drm/mediadrm/get_key_request/ok/average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+
+  value = -1;
+  EXPECT_TRUE(
+      item.getInt64("/drm/mediadrm/provide_key_response/ok/average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+}
+
+TEST_F(MediaDrmMetricsTest, AllValuesFull) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+
+  metrics.mCloseSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+  for (status_t s : {OK, UNEXPECTED_NULL}) {
+    {
+      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+      EventTimer<status_t> provide_key_response_timer(
+          &metrics.mProvideKeyResponseTiming);
+      get_key_request_timer.SetAttribute(s);
+      provide_key_response_timer.SetAttribute(s);
+    }
+  }
+
+  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);
+
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(26, item.count());
+
+  // Verify the list of pairs of int64 metrics.
+  std::vector<std::pair<std::string, int64_t>> expected_values = {
+      { "/drm/mediadrm/open_session/ok/count", 1 },
+      { "/drm/mediadrm/close_session/ok/count", 1 },
+      { "/drm/mediadrm/get_key_request/ok/count", 1 },
+      { "/drm/mediadrm/provide_key_response/ok/count", 1 },
+      { "/drm/mediadrm/get_provision_request/ok/count", 1 },
+      { "/drm/mediadrm/provide_provision_response/ok/count", 1 },
+      { "/drm/mediadrm/get_device_unique_id/ok/count", 1 },
+      { "/drm/mediadrm/open_session/error/count", 1 },
+      { "/drm/mediadrm/close_session/error/count", 1 },
+      { "/drm/mediadrm/get_key_request/error/count", 1 },
+      { "/drm/mediadrm/provide_key_response/error/count", 1 },
+      { "/drm/mediadrm/get_provision_request/error/count", 1 },
+      { "/drm/mediadrm/provide_provision_response/error/count", 1 },
+      { "/drm/mediadrm/get_device_unique_id/error/count", 1 },
+      { "/drm/mediadrm/key_status_change/USABLE/count", 1 },
+      { "/drm/mediadrm/key_status_change/EXPIRED/count", 1 },
+      { "/drm/mediadrm/key_status_change/OUTPUT_NOT_ALLOWED/count", 1 },
+      { "/drm/mediadrm/key_status_change/STATUS_PENDING/count", 1 },
+      { "/drm/mediadrm/key_status_change/INTERNAL_ERROR/count", 1 },
+      { "/drm/mediadrm/event/PROVISION_REQUIRED/count", 1 },
+      { "/drm/mediadrm/event/KEY_NEEDED/count", 1 },
+      { "/drm/mediadrm/event/KEY_EXPIRED/count", 1 },
+      { "/drm/mediadrm/event/VENDOR_DEFINED/count", 1 },
+      { "/drm/mediadrm/event/SESSION_RECLAIMED/count", 1 }};
+  for (const auto& expected_pair : expected_values) {
+    int64_t value = -1;
+    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
+        << "Failed to get " << expected_pair.first;
+    EXPECT_EQ(expected_pair.second, value)
+        << "Unexpected value for " << expected_pair.first;
+  }
+
+  // Validate timing values exist.
+  int64_t value = -1;
+  std::string name = metrics.mGetKeyRequestTiming.metric_name()
+      + "/ok/average_time_micros";
+  EXPECT_TRUE(item.getInt64(name.c_str(), &value));
+  EXPECT_GE(value, 0);
+
+  value = -1;
+  name = metrics.mProvideKeyResponseTiming.metric_name()
+      + "/ok/average_time_micros";
+  EXPECT_TRUE(item.getInt64(name.c_str(), &value));
+  EXPECT_GE(value, 0);
+}
+
+
+
+}  // namespace android
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index f2f37a6..c18d845 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -181,7 +181,8 @@
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
 
-    MediaDrmMetrics mMetrics;
+    // Mutable to allow modification within GetPropertyByteArray.
+    mutable MediaDrmMetrics mMetrics;
 
     Vector<Vector<uint8_t>> mOpenSessions;
     void closeOpenSessions();
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index f62c5f8..bb7509b 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -17,6 +17,9 @@
 #ifndef DRM_METRICS_H_
 #define DRM_METRICS_H_
 
+#include <map>
+
+#include <android/hardware/drm/1.0/types.h>
 #include <media/CounterMetric.h>
 #include <media/EventMetric.h>
 
@@ -30,12 +33,29 @@
 class MediaDrmMetrics {
  public:
   explicit MediaDrmMetrics();
-  // Counter of times openSession was called.
+  // Count of openSession calls.
   CounterMetric<status_t> mOpenSessionCounter;
-  // Counter and timing of the getKeyRequest call.
+  // Count of closeSession calls.
+  CounterMetric<status_t> mCloseSessionCounter;
+  // Count and timing of getKeyRequest calls.
   EventMetric<status_t> mGetKeyRequestTiming;
+  // Count and timing of provideKeyResponse calls.
+  EventMetric<status_t> mProvideKeyResponseTiming;
+  // Count of getProvisionRequest calls.
+  CounterMetric<status_t> mGetProvisionRequestCounter;
+  // Count of provideProvisionResponse calls.
+  CounterMetric<status_t> mProvideProvisionResponseCounter;
 
-  // TODO: Add the full set of metrics to be captured.
+  // Count of key status events broken out by status type.
+  CounterMetric<::android::hardware::drm::V1_0::KeyStatusType>
+      mKeyStatusChangeCounter;
+  // Count of events broken out by event type
+  CounterMetric<::android::hardware::drm::V1_0::EventType> mEventCounter;
+
+  // Count getPropertyByteArray calls to retrieve the device unique id.
+  CounterMetric<status_t> mGetDeviceUniqueIdCounter;
+
+  // TODO: Add session start and end time support. These are a special case.
 
   // Export the metrics to a MediaAnalyticsItem.
   void Export(MediaAnalyticsItem* item);