audio: Implement blocking in remote submix when there is no sink am: a41ff5134d

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

Change-Id: Ie71efa7ec303bb311b546e28db3d71b428c46c76
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index b2cdc28..0d50c96 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -55,8 +55,8 @@
     r_submix::AudioConfig mStreamConfig;
     std::shared_ptr<r_submix::SubmixRoute> mCurrentRoute = nullptr;
 
-    // limit for number of read error log entries to avoid spamming the logs
-    static constexpr int kMaxReadErrorLogs = 5;
+    // Limit for the number of error log entries to avoid spamming the logs.
+    static constexpr int kMaxErrorLogs = 5;
     // The duration of kMaxReadFailureAttempts * READ_ATTEMPT_SLEEP_MS must be strictly inferior
     // to the duration of a record buffer at the current record sample rate (of the device, not of
     // the recording itself). Here we have: 3 * 5ms = 15ms < 1024 frames * 1000 / 48000 = 21.333ms
@@ -68,6 +68,7 @@
     long mFramesSinceStart = 0;
     int mReadErrorCount = 0;
     int mReadFailureCount = 0;
+    int mWriteShutdownCount = 0;
 };
 
 class StreamInRemoteSubmix final : public StreamIn, public StreamSwitcher {
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 2376ed9..ca3f91a 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -134,11 +134,16 @@
     *latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
     LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
     mCurrentRoute->exitStandby(mIsInput);
-    RETURN_STATUS_IF_ERROR(mIsInput ? inRead(buffer, frameCount, actualFrameCount)
-                                    : outWrite(buffer, frameCount, actualFrameCount));
+    ::android::status_t status = mIsInput ? inRead(buffer, frameCount, actualFrameCount)
+                                          : outWrite(buffer, frameCount, actualFrameCount);
+    if ((status != ::android::OK && mIsInput) ||
+        ((status != ::android::OK && status != ::android::DEAD_OBJECT) && !mIsInput)) {
+        return status;
+    }
     mFramesSinceStart += *actualFrameCount;
-    if (!mIsInput) return ::android::OK;
-    // Only input streams need to block, for output this is implemented by MonoPipe.
+    if (!mIsInput && status != ::android::DEAD_OBJECT) return ::android::OK;
+    // Input streams always need to block, output streams need to block when there is no sink.
+    // When the sink exists, more sophisticated blocking algorithm is implemented by MonoPipe.
     const long bufferDurationUs =
             (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
     const auto totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
@@ -188,14 +193,17 @@
     if (sink != nullptr) {
         if (sink->isShutdown()) {
             sink.clear();
-            LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write";
+            if (++mWriteShutdownCount < kMaxErrorLogs) {
+                LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write. (limited logging)";
+            }
             *actualFrameCount = frameCount;
-            return ::android::OK;
+            return ::android::DEAD_OBJECT;  // Induce wait in `transfer`.
         }
     } else {
         LOG(FATAL) << __func__ << ": without a pipe!";
         return ::android::UNKNOWN_ERROR;
     }
+    mWriteShutdownCount = 0;
 
     LOG(VERBOSE) << __func__ << ": " << mDeviceAddress.toString() << ", " << frameCount
                  << " frames";
@@ -262,7 +270,7 @@
     // about to read from audio source
     sp<MonoPipeReader> source = mCurrentRoute->getSource();
     if (source == nullptr) {
-        if (++mReadErrorCount < kMaxReadErrorLogs) {
+        if (++mReadErrorCount < kMaxErrorLogs) {
             LOG(ERROR) << __func__
                        << ": no audio pipe yet we're trying to read! (not all errors will be "
                           "logged)";