audio: Adjust default impl for non-RT audio behavior of AVD

The default implementation of the primary HAL is intended to
work with the AVD. Current implementation of virtualized audio
I/O on AVD falls behind actual hardware in terms of I/O timing.
This is not tolerated by CTS tests which are tailored for
real hardware.

Make the primary HAL implementation more resilient to irregular
ALSA read/write behavior on AVD by prolonging I/O time, or
skipping operations when falling behind expected time.

Bug: 302132812
Bug: 302587331
Test: atest CtsMediaAudioTestCases
Change-Id: Ia290d9541a8a0e22d28024f7930ef554396d63c6
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;
 }