Merge "C2Decoder: Remove hardcoded PAGE_SIZE usage" into main
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index f865603..2447b18 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -478,15 +478,21 @@
                 __func__, ret.desc.toString().c_str());
         return NO_INIT;
     }
-    *outStream = sp<StreamOutHalAidl>::make(*config, std::move(context), aidlPatch.latenciesMs[0],
+    auto stream = sp<StreamOutHalAidl>::make(*config, std::move(context), aidlPatch.latenciesMs[0],
             std::move(ret.stream), mVendorExt, this /*callbackBroker*/);
-    void* cbCookie = (*outStream).get();
+    *outStream = stream;
+    /* StreamOutHalInterface* */ void* cbCookie = (*outStream).get();
     {
         std::lock_guard l(mLock);
         mCallbacks.emplace(cbCookie, Callbacks{});
         mMapper.addStream(*outStream, mixPortConfig.id, aidlPatch.id);
     }
-    if (streamCb) streamCb->setCookie(cbCookie);
+    if (streamCb) {
+        streamCb->setCookie(cbCookie);
+        // Although StreamOutHalAidl implements StreamOutHalInterfaceCallback,
+        // we always go via the CallbackBroker for consistency.
+        setStreamOutCallback(cbCookie, stream);
+    }
     eventCb->setCookie(cbCookie);
     cleanups.disarmAll();
     return OK;
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index ce56d87..3fd0911 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -82,7 +82,12 @@
           mConfig(configToBase(config)),
           mContext(std::move(context)),
           mStream(stream),
-          mVendorExt(vext) {
+          mVendorExt(vext),
+          mLastReplyLifeTimeNs(
+                  std::min(static_cast<size_t>(100),
+                          2 * mContext.getBufferDurationMs(mConfig.sample_rate))
+                  * NANOS_PER_MILLISECOND)
+{
     ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
     {
         std::lock_guard l(mLock);
@@ -285,8 +290,7 @@
     ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
     if (!mStream) return NO_INIT;
     StreamDescriptor::Reply reply;
-    // TODO: switch to updateCountersIfNeeded once we sort out mWorkerTid initialization
-    RETURN_STATUS_IF_ERROR(sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), &reply, true));
+    RETURN_STATUS_IF_ERROR(updateCountersIfNeeded(&reply));
     *frames = std::max<int64_t>(0, reply.hardware.frames);
     *timestamp = std::max<int64_t>(0, reply.hardware.timeNs);
     return OK;
@@ -409,6 +413,37 @@
     return statusTFromBinderStatus(mStream->prepareToClose());
 }
 
+void StreamHalAidl::onAsyncTransferReady() {
+    if (auto state = getState(); state == StreamDescriptor::State::TRANSFERRING) {
+        // Retrieve the current state together with position counters.
+        updateCountersIfNeeded();
+    } else {
+        ALOGW("%s: unexpected onTransferReady in the state %s", __func__, toString(state).c_str());
+    }
+}
+
+void StreamHalAidl::onAsyncDrainReady() {
+    if (auto state = getState(); state == StreamDescriptor::State::DRAINING) {
+        // Retrieve the current state together with position counters.
+        updateCountersIfNeeded();
+    } else {
+        ALOGW("%s: unexpected onDrainReady in the state %s", __func__, toString(state).c_str());
+    }
+}
+
+void StreamHalAidl::onAsyncError() {
+    std::lock_guard l(mLock);
+    if (mLastReply.state == StreamDescriptor::State::IDLE ||
+        mLastReply.state == StreamDescriptor::State::DRAINING ||
+        mLastReply.state == StreamDescriptor::State::TRANSFERRING) {
+        mLastReply.state = StreamDescriptor::State::ERROR;
+        ALOGW("%s: onError received", __func__);
+    } else {
+        ALOGW("%s: unexpected onError in the state %s", __func__,
+                toString(mLastReply.state).c_str());
+    }
+}
+
 status_t StreamHalAidl::createMmapBuffer(int32_t minSizeFrames __unused,
                                          struct audio_mmap_buffer_info *info) {
     ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
@@ -487,6 +522,7 @@
             reply->latencyMs = mLastReply.latencyMs;
         }
         mLastReply = *reply;
+        mLastReplyExpirationNs = uptimeNanos() + mLastReplyLifeTimeNs;
     }
     switch (reply->status) {
         case STATUS_OK: return OK;
@@ -502,14 +538,17 @@
 
 status_t StreamHalAidl::updateCountersIfNeeded(
         ::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply) {
-    if (mWorkerTid.load(std::memory_order_acquire) == gettid()) {
-        if (const auto state = getState(); state != StreamDescriptor::State::ACTIVE &&
-                state != StreamDescriptor::State::DRAINING &&
-                state != StreamDescriptor::State::TRANSFERRING) {
-            return sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), reply);
-        }
+    bool doUpdate = false;
+    {
+        std::lock_guard l(mLock);
+        doUpdate = uptimeNanos() > mLastReplyExpirationNs;
     }
-    if (reply != nullptr) {
+    if (doUpdate) {
+        // Since updates are paced, it is OK to perform them from any thread, they should
+        // not interfere with I/O operations of the worker.
+        return sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(),
+                reply, true /*safeFromNonWorkerThread */);
+    } else if (reply != nullptr) {  // provide cached reply
         std::lock_guard l(mLock);
         *reply = mLastReply;
     }
@@ -545,7 +584,7 @@
 
 StreamOutHalAidl::~StreamOutHalAidl() {
     if (auto broker = mCallbackBroker.promote(); broker != nullptr) {
-        broker->clearCallbacks(this);
+        broker->clearCallbacks(static_cast<StreamOutHalInterface*>(this));
     }
 }
 
@@ -614,21 +653,14 @@
 }
 
 status_t StreamOutHalAidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+    ALOGD("%p %s", this, __func__);
     TIME_CHECK();
     if (!mStream) return NO_INIT;
     if (!mContext.isAsynchronous()) {
         ALOGE("%s: the callback is intended for asynchronous streams only", __func__);
         return INVALID_OPERATION;
     }
-    if (auto broker = mCallbackBroker.promote(); broker != nullptr) {
-        if (auto cb = callback.promote(); cb != nullptr) {
-            broker->setStreamOutCallback(this, cb);
-        } else {
-            // It is expected that the framework never passes a null pointer.
-            // In the AIDL model callbacks can't be "unregistered".
-            LOG_ALWAYS_FATAL("%s: received an expired or null callback pointer", __func__);
-        }
-    }
+    mClientCallback = callback;
     return OK;
 }
 
@@ -751,7 +783,7 @@
     TIME_CHECK();
     if (!mStream) return NO_INIT;
     if (auto broker = mCallbackBroker.promote(); broker != nullptr) {
-        broker->setStreamOutEventCallback(this, callback);
+        broker->setStreamOutEventCallback(static_cast<StreamOutHalInterface*>(this), callback);
     }
     return OK;
 }
@@ -785,7 +817,8 @@
     TIME_CHECK();
     if (!mStream) return NO_INIT;
     if (auto broker = mCallbackBroker.promote(); broker != nullptr) {
-        broker->setStreamOutLatencyModeCallback(this, callback);
+        broker->setStreamOutLatencyModeCallback(
+                static_cast<StreamOutHalInterface*>(this), callback);
     }
     return OK;
 };
@@ -794,6 +827,27 @@
     return StreamHalAidl::exit();
 }
 
+void StreamOutHalAidl::onWriteReady() {
+    onAsyncTransferReady();
+    if (auto clientCb = mClientCallback.load().promote(); clientCb != nullptr) {
+        clientCb->onWriteReady();
+    }
+}
+
+void StreamOutHalAidl::onDrainReady() {
+    onAsyncDrainReady();
+    if (auto clientCb = mClientCallback.load().promote(); clientCb != nullptr) {
+        clientCb->onDrainReady();
+    }
+}
+
+void StreamOutHalAidl::onError() {
+    onAsyncError();
+    if (auto clientCb = mClientCallback.load().promote(); clientCb != nullptr) {
+        clientCb->onError();
+    }
+}
+
 status_t StreamOutHalAidl::filterAndUpdateOffloadMetadata(AudioParameter &parameters) {
     TIME_CHECK();
     bool updateMetadata = false;
diff --git a/media/libaudiohal/impl/StreamHalAidl.h b/media/libaudiohal/impl/StreamHalAidl.h
index 4acc6ac..53d46e5 100644
--- a/media/libaudiohal/impl/StreamHalAidl.h
+++ b/media/libaudiohal/impl/StreamHalAidl.h
@@ -32,6 +32,7 @@
 #include <media/audiohal/StreamHalInterface.h>
 #include <media/AidlConversionUtil.h>
 #include <media/AudioParameter.h>
+#include <mediautils/Synchronization.h>
 
 #include "ConversionHelperAidl.h"
 #include "StreamPowerLog.h"
@@ -93,6 +94,9 @@
     }
     size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
     size_t getBufferSizeFrames() const { return mBufferSizeFrames; }
+    size_t getBufferDurationMs(int32_t sampleRate) const {
+        return sampleRate != 0 ? mBufferSizeFrames * MILLIS_PER_SECOND / sampleRate : 0;
+    }
     CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
     DataMQ* getDataMQ() const { return mDataMQ.get(); }
     size_t getFrameSizeBytes() const { return mFrameSizeBytes; }
@@ -232,6 +236,10 @@
 
     status_t exit();
 
+    void onAsyncTransferReady();
+    void onAsyncDrainReady();
+    void onAsyncError();
+
     const bool mIsInput;
     const audio_config_base_t mConfig;
     const StreamContextAidl mContext;
@@ -257,8 +265,10 @@
 
     const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> mStream;
     const std::shared_ptr<::aidl::android::media::audio::IHalAdapterVendorExtension> mVendorExt;
+    const int64_t mLastReplyLifeTimeNs;
     std::mutex mLock;
     ::aidl::android::hardware::audio::core::StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
+    int64_t mLastReplyExpirationNs GUARDED_BY(mLock) = 0;
     // mStreamPowerLog is used for audio signal power logging.
     StreamPowerLog mStreamPowerLog;
     std::atomic<pid_t> mWorkerTid = -1;
@@ -266,7 +276,9 @@
 
 class CallbackBroker;
 
-class StreamOutHalAidl : public StreamOutHalInterface, public StreamHalAidl {
+class StreamOutHalAidl : public virtual StreamOutHalInterface,
+                         public virtual StreamOutHalInterfaceCallback,
+                         public StreamHalAidl {
   public:
     // Extract the output stream parameters and set by AIDL APIs.
     status_t setParameters(const String8& kvPairs) override;
@@ -344,6 +356,11 @@
 
     status_t exit() override;
 
+    // StreamOutHalInterfaceCallback
+    void onWriteReady() override;
+    void onDrainReady() override;
+    void onError() override;
+
   private:
     friend class sp<StreamOutHalAidl>;
 
@@ -352,6 +369,7 @@
 
     const std::shared_ptr<::aidl::android::hardware::audio::core::IStreamOut> mStream;
     const wp<CallbackBroker> mCallbackBroker;
+    mediautils::atomic_wp<StreamOutHalInterfaceCallback> mClientCallback;
 
     AudioOffloadMetadata mOffloadMetadata;