Merge "audio: Adjust default impl for non-RT audio behavior of AVD" into main am: 8062a77307 am: 3bb96fe48b

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2836447

Change-Id: I3ae64e1b88e9f18f859cdf8dbffb3ea83fbc4745
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index abc119c..145c3c4 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -27,13 +27,18 @@
   public:
     StreamPrimary(StreamContext* context, const Metadata& metadata);
 
+    ::android::status_t start() override;
     ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
                                  int32_t* latencyMs) override;
+    ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
 
   protected:
     std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
 
     const bool mIsAsynchronous;
+    long mStartTimeNs = 0;
+    long mFramesSinceStart = 0;
+    bool mSkipNextTransfer = false;
 };
 
 class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index 7e3bdd4..457fbb2 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include <chrono>
-
 #define LOG_TAG "AHAL_StreamPrimary"
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <audio_utils/clock.h>
+#include <error/Result.h>
 #include <error/expected_utils.h>
 
 #include "PrimaryMixer.h"
@@ -43,26 +42,52 @@
     context->startStreamDataProcessor();
 }
 
+::android::status_t StreamPrimary::start() {
+    RETURN_STATUS_IF_ERROR(StreamAlsa::start());
+    mStartTimeNs = ::android::uptimeNanos();
+    mFramesSinceStart = 0;
+    mSkipNextTransfer = false;
+    return ::android::OK;
+}
+
 ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
                                             size_t* actualFrameCount, int32_t* latencyMs) {
-    auto start = std::chrono::steady_clock::now();
-    if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
-        status != ::android::OK) {
-        return status;
-    }
     // This is a workaround for the emulator implementation which has a host-side buffer
-    // and this can result in reading faster than real time.
-    if (mIsInput && !mIsAsynchronous) {
-        auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
-                std::chrono::steady_clock::now() - start);
-        const long projectedVsObservedOffsetUs =
-                *actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() -
-                recordDurationUs.count();
-        if (projectedVsObservedOffsetUs > 0) {
-            LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us";
-            usleep(projectedVsObservedOffsetUs);
-        }
+    // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331).
+    if (!mSkipNextTransfer) {
+        RETURN_STATUS_IF_ERROR(
+                StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs));
+    } else {
+        LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)";
+        *actualFrameCount = frameCount;
+        if (mIsInput) memset(buffer, 0, frameCount);
+        mSkipNextTransfer = false;
     }
+    if (!mIsAsynchronous) {
+        const long bufferDurationUs =
+                (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
+        const auto totalDurationUs =
+                (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
+        mFramesSinceStart += *actualFrameCount;
+        const long totalOffsetUs =
+                mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
+        LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
+        if (totalOffsetUs > 0) {
+            const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs);
+            LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us";
+            usleep(sleepTimeUs);
+        } else {
+            mSkipNextTransfer = true;
+        }
+    } else {
+        LOG(VERBOSE) << __func__ << ": asynchronous transfer";
+    }
+    return ::android::OK;
+}
+
+::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) {
+    // Since not all data is actually sent to the HAL, use the position maintained by Stream class
+    // which accounts for all frames passed from / to the client.
     return ::android::OK;
 }