Move error logging to audio_utils

Test: dumpsys media.audio_flinger (instrumented)
Bug: 30572472
Change-Id: I7b00214bafa2abc2b95c022753d6dbeb58ed5c3e
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 0277ec4..badc889 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -389,58 +389,6 @@
         adev->adm_abandon_focus(adev->adm_data, in->capture_handle);
 }
 
-// Log errors: consecutive errors with the same code will
-// be aggregated if they occur within one second.
-// A mutual exclusion lock must be held before calling.
-static void log_error_l(struct error_log *log, int code) {
-    ++log->errors;
-
-    const int64_t now = audio_utils_get_real_time_ns();
-
-    // Within 1 second, cluster the same error codes together.
-    const int one_second = 1000000000;
-    if (code == log->entries[log->idx].code &&
-            now - log->entries[log->idx].last_time < one_second) {
-        log->entries[log->idx].count++;
-        log->entries[log->idx].last_time = now;
-        return;
-    }
-
-    // Add new error entry.
-    if (++log->idx >= ARRAY_SIZE(log->entries)) {
-        log->idx = 0;
-    }
-    log->entries[log->idx].count = 1;
-    log->entries[log->idx].code = code;
-    log->entries[log->idx].first_time = now;
-    log->entries[log->idx].last_time = now;
-}
-
-// Dump information in the error log. A mutual exclusion lock
-// should be held, but if that cannot be obtained, one should
-// make a copy of the error log before calling -- the call is
-// still safe, but there might be some misinterpreted data.
-static void log_dump_l(const struct error_log *log, int fd)
-{
-    dprintf(fd, "      Errors: %u\n", log->errors);
-    if (log->errors == 0)
-        return;
-
-    dprintf(fd, "      Index Code  Freq          First time           Last time\n");
-    for (size_t i = 0; i < ARRAY_SIZE(log->entries); ++i) {
-        if (log->entries[i].count != 0) {
-            char first_time[32];
-            char last_time[32];
-            audio_utils_ns_to_string(log->entries[i].first_time, first_time, sizeof(first_time));
-            audio_utils_ns_to_string(log->entries[i].last_time, last_time, sizeof(last_time));
-            dprintf(fd, "      %c%4zu %4d %5d  %s  %s\n",
-                    i == log->idx ? '*' : ' ', // mark head position
-                    i, log->entries[i].code, log->entries[i].count,
-                    first_time, last_time);
-        }
-    }
-}
-
 static int parse_snd_card_status(struct str_parms * parms, int * card,
                                  card_status_t * status)
 {
@@ -1970,13 +1918,12 @@
     dprintf(fd, "      Frames written: %lld\n", (long long)out->written);
 
     if (locked) {
-        log_dump_l(&out->error_log, fd);
         pthread_mutex_unlock(&out->lock);
-    } else {
-        // We don't have the lock here, copy for safety.
-        struct error_log log = out->error_log;
-        log_dump_l(&log, fd);
     }
+
+    // dump error info
+    (void)error_log_dump(
+            out->error_log, fd, "      " /* prefix */, 0 /* lines */, 0 /* limit_ns */);
     // dump power info (out->power_log may be null)
     (void)power_log_dump(out->power_log, fd, POWER_LOG_LINES,  0 /* limitNs */);
     return 0;
@@ -2333,7 +2280,7 @@
             out->offload_state = OFFLOAD_STATE_PLAYING;
         }
         if (ret < 0) {
-            log_error_l(&out->error_log, ERROR_CODE_WRITE);
+            error_log_log(out->error_log, ERROR_CODE_WRITE, audio_utils_get_real_time_ns());
         }
         pthread_mutex_unlock(&out->lock);
         // TODO: consider logging offload pcm
@@ -2371,7 +2318,7 @@
     const int64_t now_ns = audio_utils_get_real_time_ns();
 
     if (ret != 0) {
-        log_error_l(&out->error_log, error_code);
+        error_log_log(out->error_log, error_code, now_ns);
         if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
             ALOGE_IF(out->pcm != NULL,
                     "%s: error %zd - %s", __func__, ret, pcm_get_error(out->pcm));
@@ -3470,6 +3417,10 @@
     config->channel_mask = out->stream.common.get_channels(&out->stream.common);
     config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
 
+    out->error_log = error_log_create(
+            ERROR_LOG_ENTRIES,
+            1000000000 /* aggregate consecutive identical errors within one second in ns */);
+
     const size_t POWER_LOG_FRAMES_PER_ENTRY =
             config->sample_rate * POWER_LOG_SAMPLING_INTERVAL_MS / 1000;
     // power_log may be null if the format is not supported
@@ -3529,6 +3480,9 @@
     power_log_destroy(out->power_log);
     out->power_log = NULL;
 
+    error_log_destroy(out->error_log);
+    out->error_log = NULL;
+
     pthread_cond_destroy(&out->cond);
     pthread_mutex_destroy(&out->lock);
     free(stream);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index a612599..03f1587 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -25,6 +25,7 @@
 #include <tinycompress/tinycompress.h>
 
 #include <audio_route/audio_route.h>
+#include <audio_utils/ErrorLog.h>
 #include <audio_utils/PowerLog.h>
 #include "voice.h"
 
@@ -61,6 +62,12 @@
 #define POWER_LOG_ENTRIES (1 /* minutes */ * 60 /* seconds */ * 1000 /* msec */ \
                            / POWER_LOG_SAMPLING_INTERVAL_MS)
 
+/* Error types for the error log */
+enum {
+    ERROR_CODE_STANDBY = 1,
+    ERROR_CODE_WRITE,
+};
+
 typedef enum card_status_t {
     CARD_STATUS_OFFLINE,
     CARD_STATUS_ONLINE
@@ -160,24 +167,6 @@
     int data[];
 };
 
-enum {
-    ERROR_CODE_STANDBY,
-    ERROR_CODE_WRITE,
-};
-
-struct error_log_entry {
-    int32_t code;
-    int32_t count;
-    int64_t first_time;
-    int64_t last_time;
-};
-
-struct error_log {
-    uint32_t errors;
-    uint32_t idx;
-    struct error_log_entry entries[ERROR_LOG_ENTRIES];
-};
-
 struct stream_out {
     struct audio_stream_out stream;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -219,7 +208,7 @@
     struct audio_device *dev;
     card_status_t card_status;
 
-    struct error_log error_log;
+    error_log_t *error_log;
     power_log_t *power_log;
 };