audio: Add optional 'DriverInterface::getPosition' method.

This is a method which can be optionally implemented
by a stream in case it can provide more exact position,
for example by taking into account data in intermediate
buffers.

Implemented this method for StreamAlsa and StreamRemoteSubmix.

Bug: 264712385
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I392933f8f6b22d784726925199db00dcb0313648
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 251dea0..215de94 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -110,10 +110,12 @@
     if (isConnected) {
         reply->observable.frames = mFrameCount;
         reply->observable.timeNs = ::android::elapsedRealtimeNano();
-    } else {
-        reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
-        reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
+        if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
+            return;
+        }
     }
+    reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
+    reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
 }
 
 void StreamWorkerCommonLogic::populateReplyWrongState(
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
index ecb3c78..17c7feb 100644
--- a/audio/aidl/default/alsa/StreamAlsa.cpp
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -20,6 +20,7 @@
 #include <android-base/logging.h>
 
 #include <Utils.h>
+#include <audio_utils/clock.h>
 #include <error/expected_utils.h>
 
 #include "core-impl/StreamAlsa.h"
@@ -96,6 +97,37 @@
     return ::android::OK;
 }
 
+::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) {
+    if (mAlsaDeviceProxies.empty()) {
+        LOG(FATAL) << __func__ << ": no input devices";
+        return ::android::NO_INIT;
+    }
+    if (mIsInput) {
+        if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
+                                                 &position->timeNs);
+            ret != 0) {
+            LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
+            return ::android::INVALID_OPERATION;
+        }
+    } else {
+        uint64_t hwFrames;
+        struct timespec timestamp;
+        if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
+                                                      &timestamp);
+            ret == 0) {
+            if (hwFrames > std::numeric_limits<int64_t>::max()) {
+                hwFrames -= std::numeric_limits<int64_t>::max();
+            }
+            position->frames = static_cast<int64_t>(hwFrames);
+            position->timeNs = audio_utils_ns_from_timespec(&timestamp);
+        } else {
+            LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
+            return ::android::INVALID_OPERATION;
+        }
+    }
+    return ::android::OK;
+}
+
 void StreamAlsa::shutdown() {
     mAlsaDeviceProxies.clear();
 }
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index aaf5860..e64c578 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -184,6 +184,12 @@
     virtual ::android::status_t start() = 0;
     virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                          int32_t* latencyMs) = 0;
+    // No need to implement 'getPosition' unless the driver can provide more precise
+    // data than just total frame count. For example, the driver may correctly account
+    // for any intermediate buffers.
+    virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) {
+        return ::android::OK;
+    }
     virtual void shutdown() = 0;  // This function is only called once.
 };
 
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
index edfc728..5744d66 100644
--- a/audio/aidl/default/include/core-impl/StreamAlsa.h
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -38,6 +38,7 @@
     ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
+    ::android::status_t getPosition(StreamDescriptor::Position* position) override;
     void shutdown() override;
 
   protected:
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 2253ec7..1bca910 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -39,6 +39,7 @@
     ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
+    ::android::status_t getPosition(StreamDescriptor::Position* position) override;
     void shutdown() override;
 
     // Overridden methods of 'StreamCommonImpl', called on a Binder thread.
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 5af0d91..6d5185b 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -179,6 +179,27 @@
                      : outWrite(buffer, frameCount, actualFrameCount));
 }
 
+::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
+    sp<MonoPipeReader> source = mCurrentRoute->getSource();
+    if (source == nullptr) {
+        return ::android::NO_INIT;
+    }
+    const ssize_t framesInPipe = source->availableToRead();
+    if (framesInPipe < 0) {
+        return ::android::INVALID_OPERATION;
+    }
+    if (mIsInput) {
+        position->frames += framesInPipe;
+    } else {
+        if (position->frames > framesInPipe) {
+            position->frames -= framesInPipe;
+        } else {
+            position->frames = 0;
+        }
+    }
+    return ::android::OK;
+}
+
 // Calculate the maximum size of the pipe buffer in frames for the specified stream.
 size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
     auto pipeConfig = mCurrentRoute->mPipeConfig;