MediaMetrics: Add standard error codes

Standard error strings are added to the accepted constants.
Added mapping method from status_t to string and back.

Test: atest mediametrics_tests
Bug: 199763036
Change-Id: Iae25729b342384f1981c082661d7829f6c33f0b6
diff --git a/media/libmediametrics/MediaMetricsItem.cpp b/media/libmediametrics/MediaMetricsItem.cpp
index d597a4d..1c71f5c 100644
--- a/media/libmediametrics/MediaMetricsItem.cpp
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -23,6 +23,7 @@
 
 #include <mutex>
 #include <set>
+#include <unordered_map>
 
 #include <binder/Parcel.h>
 #include <cutils/properties.h>
@@ -51,6 +52,32 @@
 // the service is off.
 #define SVC_TRIES               2
 
+static const std::unordered_map<std::string, int32_t>& getErrorStringMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).
+    // This may be found in frameworks/av/media/libmediametrics/include/MediaMetricsConstants.h
+    static std::unordered_map<std::string, int32_t> map{
+        {"",                                      NO_ERROR},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT, BAD_VALUE},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_IO,       DEAD_OBJECT},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY,   NO_MEMORY},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY, PERMISSION_DENIED},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_STATE,    INVALID_OPERATION},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT,  WOULD_BLOCK},
+        {AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN,  UNKNOWN_ERROR},
+    };
+    return map;
+}
+
+status_t errorStringToStatus(const char *error) {
+    const auto& map = getErrorStringMap();
+    if (error == nullptr || error == "") return NO_ERROR;
+    auto it = map.find(error);
+    if (it != map.end()) {
+        return it->second;
+    }
+    return UNKNOWN_ERROR;
+}
+
 mediametrics::Item* mediametrics::Item::convert(mediametrics_handle_t handle) {
     mediametrics::Item *item = (android::mediametrics::Item *) handle;
     return item;
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index a09a673..5d0eca0 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -115,6 +115,19 @@
 #define AMEDIAMETRICS_PROP_DIRECTION      "direction"      // string AAudio input or output
 #define AMEDIAMETRICS_PROP_DURATIONNS     "durationNs"     // int64 duration time span
 #define AMEDIAMETRICS_PROP_ENCODING       "encoding"       // string value of format
+
+// Error statistics
+#define AMEDIAMETRICS_PROP_ERROR          "error#"         // string, empty or one of
+                                                           // AMEDIAMETRICS_PROP_ERROR_VALUE_*
+                                                           // Used for error categorization.
+#define AMEDIAMETRICS_PROP_ERRORSUBCODE   "errorSubCode#"  // int32, specific code for error
+                                                           // used in conjunction with error#.
+#define AMEDIAMETRICS_PROP_ERRORMESSAGE   "errorMessage#"  // string, supplemental to error.
+                                                           // Arbitrary information treated as
+                                                           // informational, may be logcat msg,
+                                                           // or an exception with stack trace.
+                                                           // Treated as "debug" information.
+
 #define AMEDIAMETRICS_PROP_EVENT          "event#"         // string value (often func name)
 #define AMEDIAMETRICS_PROP_EXECUTIONTIMENS "executionTimeNs"  // time to execute the event
 
@@ -215,4 +228,62 @@
 #define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_TONEGENERATOR "tonegenerator"  // dial tones
 #define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN       "unknown"        // callerName not set
 
+// MediaMetrics errors are expected to cover the following sources:
+// https://docs.oracle.com/javase/7/docs/api/java/lang/RuntimeException.html
+// https://docs.oracle.com/javase/7/docs/api/java/lang/Exception.html
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/Status.h;drc=88e25c0861499ee3ab885814dddc097ab234cb7b;l=57
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/AudioSystem.java;drc=3ac246c43294d7f7012bdcb0ccb7bae1aa695bd4;l=785
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libaaudio/include/aaudio/AAudio.h;drc=cfd3a6fa3aaaf712a890dc02452b38ef401083b8;l=120
+
+// Error category:
+// An empty error string indicates no error.
+
+// Error category: argument
+//   IllegalArgumentException
+//   NullPointerException
+//   BAD_VALUE
+//   Out of range, out of bounds.
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT           "argument"
+
+// Error category: io
+//   IOException
+//   android.os.DeadObjectException, android.os.RemoteException
+//   DEAD_OBJECT
+//   FAILED_TRANSACTION
+//   IO_ERROR
+//   file or ioctl failure
+//   Service, rpc, binder, or socket failure.
+//   Hardware or device failure.
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_IO                 "io"
+
+// Error category: outOfMemory
+//   OutOfMemoryException
+//   NO_MEMORY
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY             "memory"
+
+// Error category: security
+//   SecurityException
+//   PERMISSION_DENIED
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY           "security"
+
+// Error category: state
+//   IllegalStateException
+//   UnsupportedOperationException
+//   INVALID_OPERATION
+//   NO_INIT
+//   Functionality not implemented (argument may or may not be correct).
+//   Call unexpected or out of order.
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_STATE              "state"
+
+// Error category: timeout
+//   TimeoutException
+//   WOULD_BLOCK
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT            "timeout"
+
+// Error category: unknown
+//   Exception (Java specified not listed above, or custom app/service)
+//   UNKNOWN_ERROR
+//   Catch-all bucket for errors not listed above.
+#define AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN            "unknown"
+
 #endif // ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 428992c..30a2955 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -105,6 +105,36 @@
 };
 
 /*
+ * Helper for error conversions
+ */
+
+static inline constexpr const char* statusToErrorString(status_t status) {
+    switch (status) {
+    case NO_ERROR:
+        return "";
+    case BAD_VALUE:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT;
+    case DEAD_OBJECT:
+    case FAILED_TRANSACTION:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_IO;
+    case NO_MEMORY:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY;
+    case PERMISSION_DENIED:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY;
+    case NO_INIT:
+    case INVALID_OPERATION:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_STATE;
+    case WOULD_BLOCK:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT;
+    case UNKNOWN_ERROR:
+    default:
+        return AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN;
+    }
+}
+
+status_t errorStringToStatus(const char *error);
+
+/*
  * Time printing
  *
  * kPrintFormatLong time string is 19 characters (including null termination).
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 69ec947..cd6af9f 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -1225,3 +1225,29 @@
         ASSERT_EQ(id, validateId.validateId(id));
     }
 }
+
+TEST(mediametrics_tests, ErrorConversion) {
+    constexpr status_t errors[] = {
+        NO_ERROR,
+        BAD_VALUE,
+        DEAD_OBJECT,
+        NO_MEMORY,
+        PERMISSION_DENIED,
+        INVALID_OPERATION,
+        WOULD_BLOCK,
+        UNKNOWN_ERROR,
+    };
+
+    auto roundTrip = [](status_t status) {
+        return android::mediametrics::errorStringToStatus(
+                android::mediametrics::statusToErrorString(status));
+    };
+
+    // Primary status error categories.
+    for (const auto error : errors) {
+        ASSERT_EQ(error, roundTrip(error));
+    }
+
+    // Status errors specially considered.
+    ASSERT_EQ(DEAD_OBJECT, roundTrip(FAILED_TRANSACTION));
+}