NBLog periodic merging thread

Bug: 35468674
Test: no change in functionality, everything works as before
Change-Id: Id2cea243bc15767ca6803c9505bf23a18411500e
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 255e350..4e2a0d5 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -935,7 +935,6 @@
         }
         return reply.readInt64();
     }
-
 };
 
 IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -945,6 +944,29 @@
 status_t BnAudioFlinger::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
+    // Whitelist of relevant events to trigger log merging.
+    // Log merging should activate during audio activity of any kind. This are considered the
+    // most relevant events.
+    // TODO should select more wisely the items from the list
+    switch (code) {
+        case CREATE_TRACK:
+        case OPEN_RECORD:
+        case SET_MASTER_VOLUME:
+        case SET_MASTER_MUTE:
+        case SET_STREAM_VOLUME:
+        case SET_STREAM_MUTE:
+        case SET_MIC_MUTE:
+        case SET_PARAMETERS:
+        case OPEN_INPUT:
+        case SET_VOICE_VOLUME:
+        case CREATE_EFFECT:
+        case SYSTEM_READY: {
+            requestLogMerge();
+            break;
+        }
+        default:
+            break;
+    }
     switch (code) {
         case CREATE_TRACK: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index f480c16..b65ac99 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "NBLog"
 //#define LOG_NDEBUG 0
 
+#include <climits>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -958,4 +959,42 @@
     return NBLog::Entry::kOverhead + sizeof(author);
 }
 
+NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
+    : mMerger(merger),
+      mTimeoutUs(0) {}
+
+NBLog::MergeThread::~MergeThread() {
+    // set exit flag, set timeout to 0 to force threadLoop to exit and wait for the thread to join
+    requestExit();
+    setTimeoutUs(0);
+    join();
+}
+
+bool NBLog::MergeThread::threadLoop() {
+    bool doMerge;
+    {
+        AutoMutex _l(mMutex);
+        // If mTimeoutUs is negative, wait on the condition variable until it's positive.
+        // If it's positive, wait kThreadSleepPeriodUs and then merge
+        nsecs_t waitTime = mTimeoutUs > 0 ? kThreadSleepPeriodUs * 1000 : LLONG_MAX;
+        mCond.waitRelative(mMutex, waitTime);
+        doMerge = mTimeoutUs > 0;
+        mTimeoutUs -= kThreadSleepPeriodUs;
+    }
+    if (doMerge) {
+        mMerger.merge();
+    }
+    return true;
+}
+
+void NBLog::MergeThread::wakeup() {
+    setTimeoutUs(kThreadWakeupPeriodUs);
+}
+
+void NBLog::MergeThread::setTimeoutUs(int time) {
+    AutoMutex _l(mMutex);
+    mTimeoutUs = time;
+    mCond.signal();
+}
+
 }   // namespace android
diff --git a/media/libnbaio/include/NBLog.h b/media/libnbaio/include/NBLog.h
index 043f15e..369c22a 100644
--- a/media/libnbaio/include/NBLog.h
+++ b/media/libnbaio/include/NBLog.h
@@ -20,8 +20,9 @@
 #define ANDROID_MEDIA_NBLOG_H
 
 #include <binder/IMemory.h>
-#include <utils/Mutex.h>
 #include <audio_utils/fifo.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
 
 #include <vector>
 
@@ -41,6 +42,7 @@
 enum Event {
     EVENT_RESERVED,
     EVENT_STRING,               // ASCII string, not NUL-terminated
+    // TODO: make timestamp optional
     EVENT_TIMESTAMP,            // clock_gettime(CLOCK_MONOTONIC)
     EVENT_INTEGER,              // integer value entry
     EVENT_FLOAT,                // floating point value entry
@@ -106,7 +108,7 @@
 
         template<typename T>
         inline const T& payload() {
-            return *reinterpret_cast<const T *>(ptr + 2);
+            return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
         }
 
     private:
@@ -425,6 +427,43 @@
     size_t handleAuthor(const FormatEntry &fmtEntry, String8 *body);
 };
 
+// MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
+// when triggered, it awakes for a lapse of time, during which it periodically merges; if
+// retriggered, the timeout is reset.
+// The thread is triggered on AudioFlinger binder activity.
+class MergeThread : public Thread {
+public:
+    MergeThread(Merger &merger);
+    virtual ~MergeThread() override;
+
+    // Reset timeout and activate thread to merge periodically if it's idle
+    void wakeup();
+
+    // Set timeout period until the merging thread goes idle again
+    void setTimeoutUs(int time);
+
+private:
+    virtual bool threadLoop() override;
+
+    // the merger who actually does the work of merging the logs
+    Merger&     mMerger;
+
+    // mutex for the condition variable
+    Mutex       mMutex;
+
+    // condition variable to activate merging on timeout >= 0
+    Condition   mCond;
+
+    // time left until the thread blocks again (in microseconds)
+    int         mTimeoutUs;
+
+    // merging period when the thread is awake
+    static const int  kThreadSleepPeriodUs = 1000000 /*1s*/;
+
+    // initial timeout value when triggered
+    static const int  kThreadWakeupPeriodUs = 3000000 /*3s*/;
+};
+
 };  // class NBLog
 
 }   // namespace android