Add CounterMetric and a single use case.

This adds a new class CounterMetric which is used to hold metric
information.  It also adds a single use case for recording and
reporting a counter metric.

BUG: 64001676

Test: Added and ran a new test for the specific CounterMetric use case.
Change-Id: I39cab8328f135e579f80b6c2fab721da5f569795
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index c4b197c..9fc7d04 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -228,6 +228,7 @@
 DrmHal::DrmHal()
    : mDrmSessionClient(new DrmSessionClient(this)),
      mFactories(makeDrmFactories()),
+     mOpenSessionCounter("/drm/mediadrm/open_session", "status"),
      mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
 }
 
@@ -518,6 +519,8 @@
                 mDrmSessionClient, sessionId);
         mOpenSessions.push(sessionId);
     }
+
+    mOpenSessionCounter.Increment(err);
     return err;
 }
 
@@ -946,8 +949,25 @@
 }
 
 status_t DrmHal::getMetrics(MediaAnalyticsItem* metrics) {
-    // TODO: Replace this with real metrics.
-    metrics->setCString("/drm/mediadrm/dummymetric", "dummy");
+    // TODO: Move mOpenSessionCounter and suffixes to a separate class
+    // that manages the collection of metrics and exporting them.
+    std::string success_count_name =
+        mOpenSessionCounter.metric_name() + "/ok/count";
+    std::string error_count_name =
+        mOpenSessionCounter.metric_name() + "/error/count";
+    mOpenSessionCounter.ExportValues(
+        [&] (status_t status, int64_t value) {
+            if (status == OK) {
+                metrics->setInt64(success_count_name.c_str(), value);
+            } else {
+                int64_t total_errors(0);
+                metrics->getInt64(error_count_name.c_str(), &total_errors);
+                metrics->setInt64(error_count_name.c_str(),
+                                  total_errors + value);
+                // TODO: Add support for exporting the list of error values.
+                // This probably needs to be added to MediaAnalyticsItem.
+            }
+        });
     return OK;
 }
 
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
new file mode 100644
index 0000000..674d3eb
--- /dev/null
+++ b/drm/libmediadrm/tests/Android.bp
@@ -0,0 +1,12 @@
+// Build definitions for unit tests.
+
+cc_test {
+    name: "CounterMetric_test",
+    srcs: ["CounterMetric_test.cpp"],
+    shared_libs: ["libmediadrm"],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
diff --git a/drm/libmediadrm/tests/CounterMetric_test.cpp b/drm/libmediadrm/tests/CounterMetric_test.cpp
new file mode 100644
index 0000000..6bca0da
--- /dev/null
+++ b/drm/libmediadrm/tests/CounterMetric_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "CounterMetric.h"
+
+namespace android {
+
+/**
+ * Unit tests for the CounterMetric class.
+ */
+class CounterMetricTest : public ::testing::Test {
+};
+
+TEST_F(CounterMetricTest, IntDataTypeEmpty) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  EXPECT_TRUE(values.empty());
+}
+
+TEST_F(CounterMetricTest, IntDataType) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.Increment(7);
+  metric.Increment(8);
+  metric.Increment(8);
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values[7]);
+  EXPECT_EQ(2, values[8]);
+}
+
+TEST_F(CounterMetricTest, StringDataType) {
+  CounterMetric<std::string> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<std::string, int64_t> values;
+
+  metric.Increment("a");
+  metric.Increment("b");
+  metric.Increment("b");
+
+  metric.ExportValues(
+      [&] (std::string attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values["a"]);
+  EXPECT_EQ(2, values["b"]);
+}
+
+}  // namespace android
diff --git a/include/media/CounterMetric.h b/include/media/CounterMetric.h
new file mode 120000
index 0000000..baba043
--- /dev/null
+++ b/include/media/CounterMetric.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/CounterMetric.h
\ No newline at end of file
diff --git a/media/libmedia/include/media/CounterMetric.h b/media/libmedia/include/media/CounterMetric.h
new file mode 100644
index 0000000..f39ca7c
--- /dev/null
+++ b/media/libmedia/include/media/CounterMetric.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 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.
+ */
+#ifndef ANDROID_COUNTER_METRIC_H_
+#define ANDROID_COUNTER_METRIC_H_
+
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Log.h>
+
+namespace android {
+
+
+// The CounterMetric class is used to hold counts of operations or events.
+// A CounterMetric can break down counts by a dimension specified by the
+// application. E.g. an application may want to track counts broken out by
+// error code or the size of some parameter.
+//
+// Example:
+//
+//   CounterMetric<status_t> workCounter;
+//   workCounter("workCounterName", "result_status");
+//
+//   status_t err = DoWork();
+//
+//   // Increments the number of times called with the given error code.
+//   workCounter.Increment(err);
+//
+//   std::map<int, int64_t> values;
+//    metric.ExportValues(
+//        [&] (int attribute_value, int64_t value) {
+//             values[attribute_value] = value;
+//        });
+//
+//   // Do something with the exported stat.
+//
+template<typename AttributeType>
+class CounterMetric {
+ public:
+  // Instantiate the counter with the given metric name and
+  // attribute names. |attribute_names| must not be null.
+  CounterMetric(
+      const std::string& metric_name,
+      const std::string& attribute_name)
+          : metric_name_(metric_name),
+            attribute_name_(attribute_name) {}
+
+  // Increment the count of times the operation occurred with this
+  // combination of attributes.
+  void Increment(AttributeType attribute) {
+    if (values_.find(attribute) == values_.end()) {
+      values_[attribute] = 1;
+    } else {
+      values_[attribute] = values_[attribute] + 1;
+    }
+  };
+
+  // Export the metrics to the provided |function|. Each value for Attribute
+  // has a separate count. As such, |function| will be called once per value
+  // of Attribute.
+  void ExportValues(
+      std::function<void (const AttributeType&,
+                          const int64_t count)> function) {
+    for (auto it = values_.begin(); it != values_.end(); it++) {
+      function(it->first, it->second);
+    }
+  }
+
+  const std::string& metric_name() { return metric_name_; };
+
+ private:
+  const std::string metric_name_;
+  const std::string attribute_name_;
+  std::map<AttributeType, int64_t> values_;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_COUNTER_METRIC_H_
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 1a0553e..f2b25cd 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -23,6 +23,7 @@
 #include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.0/IDrmFactory.h>
 
+#include <media/CounterMetric.h>
 #include <media/IDrm.h>
 #include <media/IDrmClient.h>
 #include <media/MediaAnalyticsItem.h>
@@ -180,6 +181,8 @@
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
 
+    CounterMetric<status_t> mOpenSessionCounter;
+
     Vector<Vector<uint8_t>> mOpenSessions;
     void closeOpenSessions();