aaudio: improve accuracy of timestamps

Account for latency added by the AAudio service.
Fix input timestamps.

Bug: 37080396
Test: test_timestamps.cpp input_monitor.cpp
Change-Id: I1053cd21af722bb9b9371df4e5731bf4a0a57b0b
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index b4377fb..54e8001 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -28,7 +28,6 @@
 // Used to send information about the HAL to the client.
 struct AAudioMessageTimestamp {
     int64_t position;     // number of frames transferred so far
-    int64_t deviceOffset; // add to client position to get device position
     int64_t timestamp;    // time when that position was reached
 };
 
@@ -51,7 +50,8 @@
 typedef struct AAudioServiceMessage_s {
     enum class code : uint32_t {
         NOTHING,
-        TIMESTAMP,
+        TIMESTAMP_SERVICE, // when frame is read or written by the service to the client
+        TIMESTAMP_HARDWARE, // when frame is at DAC or ADC
         EVENT,
     };
 
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4c7d0f7..259c9b9 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -68,6 +68,7 @@
         , mServiceInterface(serviceInterface)
         , mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
         , mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
+        , mAtomicTimestamp()
         {
     ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
           mWakeupDelayNanos, mMinimumSleepNanos);
@@ -351,12 +352,15 @@
 aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
                            int64_t *framePosition,
                            int64_t *timeNanoseconds) {
-    // TODO Generate in server and pass to client. Return latest.
-    int64_t time = AudioClock::getNanoseconds();
-    *framePosition = mClockModel.convertTimeToPosition(time) + mFramesOffsetFromService;
-    // TODO Get a more accurate timestamp from the service. This code just adds a fudge factor.
-    *timeNanoseconds = time + (6 * AAUDIO_NANOS_PER_MILLISECOND);
-    return AAUDIO_OK;
+    // Generated in server and passed to client. Return latest.
+    if (mAtomicTimestamp.isValid()) {
+        Timestamp timestamp = mAtomicTimestamp.read();
+        *framePosition = timestamp.getPosition();
+        *timeNanoseconds = timestamp.getNanoseconds();
+        return AAUDIO_OK;
+    } else {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
 }
 
 aaudio_result_t AudioStreamInternal::updateStateWhileWaiting() {
@@ -385,7 +389,7 @@
     oldTime = nanoTime;
 }
 
-aaudio_result_t AudioStreamInternal::onTimestampFromServer(AAudioServiceMessage *message) {
+aaudio_result_t AudioStreamInternal::onTimestampService(AAudioServiceMessage *message) {
 #if LOG_TIMESTAMPS
     logTimestamp(*message);
 #endif
@@ -393,6 +397,12 @@
     return AAUDIO_OK;
 }
 
+aaudio_result_t AudioStreamInternal::onTimestampHardware(AAudioServiceMessage *message) {
+    Timestamp timestamp(message->timestamp.position, message->timestamp.timestamp);
+    mAtomicTimestamp.write(timestamp);
+    return AAUDIO_OK;
+}
+
 aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *message) {
     aaudio_result_t result = AAUDIO_OK;
     switch (message->event.event) {
@@ -456,8 +466,12 @@
             break; // no command this time, no problem
         }
         switch (message.what) {
-        case AAudioServiceMessage::code::TIMESTAMP:
-            result = onTimestampFromServer(&message);
+        case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
+            result = onTimestampService(&message);
+            break;
+
+        case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
+            result = onTimestampHardware(&message);
             break;
 
         case AAudioServiceMessage::code::EVENT:
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 1b991de..607e734 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -122,7 +122,9 @@
 
     aaudio_result_t onEventFromServer(AAudioServiceMessage *message);
 
-    aaudio_result_t onTimestampFromServer(AAudioServiceMessage *message);
+    aaudio_result_t onTimestampService(AAudioServiceMessage *message);
+
+    aaudio_result_t onTimestampHardware(AAudioServiceMessage *message);
 
     void logTimestamp(AAudioServiceMessage &message);
 
@@ -181,6 +183,11 @@
 
     AudioEndpointParcelable  mEndPointParcelable; // description of the buffers filled by service
     EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
+
+    SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
+
+    int64_t                  mServiceLatencyNanos = 0;
+
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index acd319b..b0c6c94 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -258,4 +258,74 @@
     }
 }
 
+
+/**
+ * Simple double buffer for a structure that can be written occasionally and read occasionally.
+ * This allows a SINGLE writer with multiple readers.
+ *
+ * It is OK if the FIFO overflows and we lose old values.
+ * It is also OK if we read an old value.
+ * Thread may return a non-atomic result if the other thread is rapidly writing
+ * new values on another core.
+ */
+template <class T>
+class SimpleDoubleBuffer {
+public:
+    SimpleDoubleBuffer()
+            : mValues()
+            , mCounter(0) {}
+
+    __attribute__((no_sanitize("integer")))
+    void write(T value) {
+        int index = mCounter.load() & 1;
+        mValues[index] = value;
+        mCounter++; // Increment AFTER updating storage, OK if it wraps.
+    }
+
+    T read() const {
+        T result;
+        int before;
+        int after;
+        int timeout = 3;
+        do {
+            // Check to see if a write occurred while were reading.
+            before = mCounter.load();
+            int index = (before & 1) ^ 1;
+            result = mValues[index];
+            after = mCounter.load();
+        } while ((after != before) && --timeout > 0);
+        return result;
+    }
+
+    /**
+     * @return true if at least one value has been written
+     */
+    bool isValid() const {
+        return mCounter.load() > 0;
+    }
+
+private:
+    T                    mValues[2];
+    std::atomic<int>     mCounter;
+};
+
+class Timestamp {
+public:
+    Timestamp()
+            : mPosition(0)
+            , mNanoseconds(0) {}
+    Timestamp(int64_t position, int64_t nanoseconds)
+            : mPosition(position)
+            , mNanoseconds(nanoseconds) {}
+
+    int64_t getPosition() const { return mPosition; }
+
+    int64_t getNanoseconds() const { return mNanoseconds; }
+
+private:
+    // These cannot be const because we need to implement the copy assignment operator.
+    int64_t mPosition;
+    int64_t mNanoseconds;
+};
+
 #endif //UTILITY_AAUDIO_UTILITIES_H