[go: nahoru, domu]

audio: fix mmap output

1. add createMmapBuffer() for vendor to override and create mmap fd.
2. add refineMmapPosition() for vendor to override and update
latency in mmap case.
3. fix testcases position check in mmap case.

Bug: 274456992
Bug: 345591089
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ie63fdd47c0ddc563d84699dfdf6d4e9b72b5af43
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index a1008a4..dd216e5 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -26,6 +26,7 @@
 #include <aidl/android/media/audio/common/AudioDeviceType.h>
 #include <aidl/android/media/audio/common/AudioFormatDescription.h>
 #include <aidl/android/media/audio/common/AudioInputFlags.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
 #include <aidl/android/media/audio/common/AudioMode.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 #include <aidl/android/media/audio/common/PcmType.h>
@@ -188,4 +189,15 @@
     return frameCountFromDurationUs(durationMs * 1000, sampleRateHz);
 }
 
+constexpr bool hasMmapFlag(const ::aidl::android::media::audio::common::AudioIoFlags& flags) {
+    return (flags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::Tag::input &&
+            isBitPositionFlagSet(
+                    flags.get<::aidl::android::media::audio::common::AudioIoFlags::Tag::input>(),
+                    ::aidl::android::media::audio::common::AudioInputFlags::MMAP_NOIRQ)) ||
+           (flags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::Tag::output &&
+            isBitPositionFlagSet(
+                    flags.get<::aidl::android::media::audio::common::AudioIoFlags::Tag::output>(),
+                    ::aidl::android::media::audio::common::AudioOutputFlags::MMAP_NOIRQ));
+}
+
 }  // namespace aidl::android::hardware::audio::common
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 0d6151e..543efd1 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -36,6 +36,7 @@
 
 using aidl::android::hardware::audio::common::frameCountFromDurationMs;
 using aidl::android::hardware::audio::common::getFrameSizeInBytes;
+using aidl::android::hardware::audio::common::hasMmapFlag;
 using aidl::android::hardware::audio::common::isBitPositionFlagSet;
 using aidl::android::hardware::audio::common::isValidAudioMode;
 using aidl::android::hardware::audio::common::SinkMetadata;
@@ -205,35 +206,31 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     const auto& flags = portConfigIt->flags.value();
-    if ((flags.getTag() == AudioIoFlags::Tag::input &&
-         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::input>(),
-                               AudioInputFlags::MMAP_NOIRQ)) ||
-        (flags.getTag() == AudioIoFlags::Tag::output &&
-         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
-                               AudioOutputFlags::MMAP_NOIRQ))) {
-        StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
-                                              mVendorDebug.forceTransientBurst,
-                                              mVendorDebug.forceSynchronousDrain};
-        std::shared_ptr<ISoundDose> soundDose;
-        if (!getSoundDose(&soundDose).isOk()) {
-            LOG(ERROR) << __func__ << ": could not create sound dose instance";
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
-        StreamContext temp(
-                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
-                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
-                portConfigIt->format.value(), portConfigIt->channelMask.value(),
-                portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
-                portConfigIt->ext.get<AudioPortExt::mix>().handle,
-                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
-                asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
-        if (temp.isValid()) {
-            *out_context = std::move(temp);
-        } else {
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
+    StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
+                                          mVendorDebug.forceTransientBurst,
+                                          mVendorDebug.forceSynchronousDrain};
+    std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
+    std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
+    std::shared_ptr<ISoundDose> soundDose;
+    if (!getSoundDose(&soundDose).isOk()) {
+        LOG(ERROR) << __func__ << ": could not create sound dose instance";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (!hasMmapFlag(flags)) {
+        dataMQ = std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames);
+        streamAsyncCallback = asyncCallback;
+    }
+    StreamContext temp(
+            std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
+            std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
+            portConfigIt->format.value(), portConfigIt->channelMask.value(),
+            portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
+            portConfigIt->ext.get<AudioPortExt::mix>().handle, std::move(dataMQ),
+            streamAsyncCallback, outEventCallback, mSoundDose.getInstance(), params);
+    if (temp.isValid()) {
+        *out_context = std::move(temp);
     } else {
-        // TODO: Implement simulation of MMAP buffer allocation
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     return ndk::ScopedAStatus::ok();
 }
@@ -373,6 +370,13 @@
     return kLatencyMs;
 }
 
+ndk::ScopedAStatus Module::createMmapBuffer(
+        const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
+        ::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
+    LOG(ERROR) << __func__ << ": " << mType << ": is not implemented";
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
     std::vector<AudioRoute*> result;
     auto& routes = getConfig().routes;
@@ -866,6 +870,9 @@
     RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
                                                nullptr, nullptr, &context));
     context.fillDescriptor(&_aidl_return->desc);
+    if (hasMmapFlag(context.getFlags())) {
+        RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
+    }
     std::shared_ptr<StreamIn> stream;
     RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
                                              getMicrophoneInfos(), &stream));
@@ -913,6 +920,9 @@
                                                isNonBlocking ? in_args.callback : nullptr,
                                                in_args.eventCallback, &context));
     context.fillDescriptor(&_aidl_return->desc);
+    if (hasMmapFlag(context.getFlags())) {
+        RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
+    }
     std::shared_ptr<StreamOut> stream;
     RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
                                               in_args.offloadInfo, &stream));
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 31b0645..8f5e839 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -30,6 +30,7 @@
 using aidl::android::hardware::audio::common::AudioOffloadMetadata;
 using aidl::android::hardware::audio::common::getChannelCount;
 using aidl::android::hardware::audio::common::getFrameSizeInBytes;
+using aidl::android::hardware::audio::common::hasMmapFlag;
 using aidl::android::hardware::audio::common::isBitPositionFlagSet;
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
@@ -84,7 +85,7 @@
         LOG(ERROR) << "frame size is invalid";
         return false;
     }
-    if (mDataMQ && !mDataMQ->isValid()) {
+    if (!hasMmapFlag(mFlags) && mDataMQ && !mDataMQ->isValid()) {
         LOG(ERROR) << "data FMQ is invalid";
         return false;
     }
@@ -116,17 +117,19 @@
 std::string StreamWorkerCommonLogic::init() {
     if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
     if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
-    StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
-    if (dataMQ == nullptr) return "Data MQ is null";
-    if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
-        return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
-    }
-    mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
-    mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
-    if (mDataBuffer == nullptr) {
-        return "Failed to allocate data buffer for element count " +
-               std::to_string(dataMQ->getQuantumCount()) +
-               ", size in bytes: " + std::to_string(mDataBufferSize);
+    if (!hasMmapFlag(mContext->getFlags())) {
+        StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+        if (dataMQ == nullptr) return "Data MQ is null";
+        if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
+            return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
+        }
+        mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
+        mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
+        if (mDataBuffer == nullptr) {
+            return "Failed to allocate data buffer for element count " +
+                   std::to_string(dataMQ->getQuantumCount()) +
+                   ", size in bytes: " + std::to_string(mDataBufferSize);
+        }
     }
     if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
         return "Failed to initialize the driver: " + std::to_string(status);
@@ -136,16 +139,26 @@
 
 void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
                                             bool isConnected) const {
+    static const StreamDescriptor::Position kUnknownPosition = {
+            .frames = StreamDescriptor::Position::UNKNOWN,
+            .timeNs = StreamDescriptor::Position::UNKNOWN};
     reply->status = STATUS_OK;
     if (isConnected) {
         reply->observable.frames = mContext->getFrameCount();
         reply->observable.timeNs = ::android::uptimeNanos();
-        if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
-            return;
+        if (auto status = mDriver->refinePosition(&reply->observable); status != ::android::OK) {
+            reply->observable = kUnknownPosition;
+        }
+    } else {
+        reply->observable = reply->hardware = kUnknownPosition;
+    }
+    if (hasMmapFlag(mContext->getFlags())) {
+        if (auto status = mDriver->getMmapPositionAndLatency(&reply->hardware, &reply->latencyMs);
+            status != ::android::OK) {
+            reply->hardware = kUnknownPosition;
+            reply->latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
         }
     }
-    reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
-    reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
 }
 
 void StreamWorkerCommonLogic::populateReplyWrongState(
@@ -224,7 +237,9 @@
                     mState == StreamDescriptor::State::ACTIVE ||
                     mState == StreamDescriptor::State::PAUSED ||
                     mState == StreamDescriptor::State::DRAINING) {
-                    if (!read(fmqByteCount, &reply)) {
+                    if (hasMmapFlag(mContext->getFlags())) {
+                        populateReply(&reply, mIsConnected);
+                    } else if (!read(fmqByteCount, &reply)) {
                         mState = StreamDescriptor::State::ERROR;
                     }
                     if (mState == StreamDescriptor::State::IDLE ||
@@ -470,7 +485,9 @@
                 if (mState != StreamDescriptor::State::ERROR &&
                     mState != StreamDescriptor::State::TRANSFERRING &&
                     mState != StreamDescriptor::State::TRANSFER_PAUSED) {
-                    if (!write(fmqByteCount, &reply)) {
+                    if (hasMmapFlag(mContext->getFlags())) {
+                        populateReply(&reply, mIsConnected);
+                    } else if (!write(fmqByteCount, &reply)) {
                         mState = StreamDescriptor::State::ERROR;
                     }
                     std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
@@ -657,6 +674,7 @@
         const std::shared_ptr<StreamCommonInterface>& delegate) {
     mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
     if (!mWorker->start()) {
+        LOG(ERROR) << __func__ << ": Worker start error: " << mWorker->getError();
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     if (auto flags = getContext().getFlags();
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index a326217..00eeb4e 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -205,6 +205,9 @@
     virtual std::unique_ptr<Configuration> initializeConfig();
     virtual int32_t getNominalLatencyMs(
             const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
+    virtual ndk::ScopedAStatus createMmapBuffer(
+            const ::aidl::android::hardware::audio::core::StreamContext& context,
+            ::aidl::android::hardware::audio::core::StreamDescriptor* desc);
 
     // Utility and helper functions accessible to subclasses.
     static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 21e63f9..6b45866 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -177,6 +177,11 @@
     virtual ::android::status_t refinePosition(StreamDescriptor::Position* /*position*/) {
         return ::android::OK;
     }
+    // Implement 'getMmapPositionAndLatency' is necessary if driver can support mmap stream.
+    virtual ::android::status_t getMmapPositionAndLatency(StreamDescriptor::Position* /*position*/,
+                                                          int32_t* /*latency*/) {
+        return ::android::OK;
+    }
     virtual void shutdown() = 0;  // This function is only called once.
 };
 
@@ -241,6 +246,7 @@
     virtual bool start() = 0;
     virtual pid_t getTid() = 0;
     virtual void stop() = 0;
+    virtual std::string getError() = 0;
 };
 
 template <class WorkerLogic>
@@ -260,6 +266,7 @@
     }
     pid_t getTid() override { return WorkerImpl::getTid(); }
     void stop() override { return WorkerImpl::stop(); }
+    std::string getError() override { return WorkerImpl::getError(); }
 };
 
 class StreamInWorkerLogic : public StreamWorkerCommonLogic {
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index d576c7c..bbc4caf 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -56,6 +56,7 @@
 using namespace android;
 using aidl::android::hardware::audio::common::AudioOffloadMetadata;
 using aidl::android::hardware::audio::common::getChannelCount;
+using aidl::android::hardware::audio::common::hasMmapFlag;
 using aidl::android::hardware::audio::common::isAnyBitPositionFlagSet;
 using aidl::android::hardware::audio::common::isBitPositionFlagSet;
 using aidl::android::hardware::audio::common::isTelephonyDeviceType;
@@ -637,19 +638,39 @@
           mCommandMQ(new CommandMQ(descriptor.command)),
           mReplyMQ(new ReplyMQ(descriptor.reply)),
           mBufferSizeFrames(descriptor.bufferSizeFrames),
-          mDataMQ(maybeCreateDataMQ(descriptor)) {}
+          mDataMQ(maybeCreateDataMQ(descriptor)),
+          mIsMmapped(isMmapped(descriptor)),
+          mSharedMemoryFd(maybeGetMmapFd(descriptor)) {
+        if (isMmapped()) {
+            mSharedMemory = (int8_t*)mmap(nullptr, getBufferSizeBytes(), PROT_READ | PROT_WRITE,
+                                          MAP_SHARED, mSharedMemoryFd, 0);
+            if (mSharedMemory == MAP_FAILED) {
+                PLOG(ERROR) << __func__ << ": mmap() failed.";
+                mSharedMemory = nullptr;
+            }
+        }
+    }
+    ~StreamContext() {
+        if (mSharedMemory != nullptr) {
+            munmap(mSharedMemory, getBufferSizeBytes());
+        }
+    }
     void checkIsValid() const {
         EXPECT_NE(0UL, mFrameSizeBytes);
         ASSERT_NE(nullptr, mCommandMQ);
         EXPECT_TRUE(mCommandMQ->isValid());
         ASSERT_NE(nullptr, mReplyMQ);
         EXPECT_TRUE(mReplyMQ->isValid());
-        if (mDataMQ != nullptr) {
-            EXPECT_TRUE(mDataMQ->isValid());
-            EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
-                      mFrameSizeBytes * mBufferSizeFrames)
-                    << "Data MQ actual buffer size is "
-                       "less than the buffer size as specified by the descriptor";
+        if (isMmapped()) {
+            ASSERT_NE(nullptr, mSharedMemory);
+        } else {
+            if (mDataMQ != nullptr) {
+                EXPECT_TRUE(mDataMQ->isValid());
+                EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
+                          mFrameSizeBytes * mBufferSizeFrames)
+                        << "Data MQ actual buffer size is "
+                           "less than the buffer size as specified by the descriptor";
+            }
         }
     }
     size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
@@ -658,6 +679,8 @@
     DataMQ* getDataMQ() const { return mDataMQ.get(); }
     size_t getFrameSizeBytes() const { return mFrameSizeBytes; }
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+    bool isMmapped() const { return mIsMmapped; }
+    int8_t* getMmapMemory() const { return mSharedMemory; }
 
   private:
     static std::unique_ptr<DataMQ> maybeCreateDataMQ(const StreamDescriptor& descriptor) {
@@ -667,12 +690,26 @@
         }
         return nullptr;
     }
+    static bool isMmapped(const StreamDescriptor& descriptor) {
+        using Tag = StreamDescriptor::AudioBuffer::Tag;
+        return descriptor.audio.getTag() == Tag::mmap;
+    }
+    static int32_t maybeGetMmapFd(const StreamDescriptor& descriptor) {
+        using Tag = StreamDescriptor::AudioBuffer::Tag;
+        if (descriptor.audio.getTag() == Tag::mmap) {
+            return descriptor.audio.get<Tag::mmap>().sharedMemory.fd.get();
+        }
+        return -1;
+    }
 
     const size_t mFrameSizeBytes;
     std::unique_ptr<CommandMQ> mCommandMQ;
     std::unique_ptr<ReplyMQ> mReplyMQ;
     const size_t mBufferSizeFrames;
     std::unique_ptr<DataMQ> mDataMQ;
+    const bool mIsMmapped;
+    const int32_t mSharedMemoryFd;
+    int8_t* mSharedMemory = nullptr;
 };
 
 struct StreamEventReceiver {
@@ -868,12 +905,15 @@
           mDataMQ(context.getDataMQ()),
           mData(context.getBufferSizeBytes()),
           mDriver(driver),
-          mEventReceiver(eventReceiver) {}
+          mEventReceiver(eventReceiver),
+          mIsMmapped(context.isMmapped()),
+          mSharedMemory(context.getMmapMemory()) {}
     StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
     StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
     StreamContext::DataMQ* getDataMQ() const { return mDataMQ; }
     StreamLogicDriver* getDriver() const { return mDriver; }
     StreamEventReceiver* getEventReceiver() const { return mEventReceiver; }
+    bool isMmapped() const { return mIsMmapped; }
 
     std::string init() override {
         LOG(DEBUG) << __func__;
@@ -914,6 +954,22 @@
         LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
         return false;
     }
+    bool readDataFromMmap(size_t readCount) {
+        if (mSharedMemory != nullptr) {
+            std::memcpy(mData.data(), mSharedMemory, readCount);
+            return true;
+        }
+        LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from mmap failed";
+        return false;
+    }
+    bool writeDataToMmap() {
+        if (mSharedMemory != nullptr) {
+            std::memcpy(mSharedMemory, mData.data(), mData.size());
+            return true;
+        }
+        LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to mmap failed";
+        return false;
+    }
 
   private:
     StreamContext::CommandMQ* mCommandMQ;
@@ -923,6 +979,8 @@
     StreamLogicDriver* const mDriver;
     StreamEventReceiver* const mEventReceiver;
     int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
+    const bool mIsMmapped;
+    int8_t* mSharedMemory = nullptr;
 };
 
 class StreamReaderLogic : public StreamCommonLogic {
@@ -970,7 +1028,8 @@
                        << ": received invalid byte count in the reply: " << reply.fmqByteCount;
             return Status::ABORT;
         }
-        if (static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->availableToRead()) {
+        if (!isMmapped() &&
+            static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->availableToRead()) {
             LOG(ERROR) << __func__
                        << ": the byte count in the reply is not the same as the amount of "
                        << "data available in the MQ: " << reply.fmqByteCount
@@ -991,8 +1050,10 @@
             return Status::ABORT;
         }
         const bool acceptedReply = getDriver()->processValidReply(reply);
-        if (const size_t readCount = getDataMQ()->availableToRead(); readCount > 0) {
-            if (readDataFromMQ(readCount)) {
+        if (const size_t readCount =
+                    !isMmapped() ? getDataMQ()->availableToRead() : reply.fmqByteCount;
+            readCount > 0) {
+            if (isMmapped() ? readDataFromMmap(readCount) : readDataFromMQ(readCount)) {
                 goto checkAcceptedReply;
             }
             LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
@@ -1028,8 +1089,10 @@
             LOG(ERROR) << __func__ << ": no next command";
             return Status::ABORT;
         }
-        if (actualSize != 0 && !writeDataToMQ()) {
-            return Status::ABORT;
+        if (actualSize != 0) {
+            if (isMmapped() ? !writeDataToMmap() : !writeDataToMQ()) {
+                return Status::ABORT;
+            }
         }
         LOG(DEBUG) << "Writing command: " << command.toString();
         if (!getCommandMQ()->writeBlocking(&command, 1)) {
@@ -1058,7 +1121,7 @@
             return Status::ABORT;
         }
         // It is OK for the implementation to leave data in the MQ when the stream is paused.
-        if (reply.state != StreamDescriptor::State::PAUSED &&
+        if (!isMmapped() && reply.state != StreamDescriptor::State::PAUSED &&
             getDataMQ()->availableToWrite() != getDataMQ()->getQuantumCount()) {
             LOG(ERROR) << __func__ << ": the HAL module did not consume all data from the data MQ: "
                        << "available to write " << getDataMQ()->availableToWrite()
@@ -2904,15 +2967,24 @@
 
 class StreamLogicDefaultDriver : public StreamLogicDriver {
   public:
-    StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands, size_t frameSizeBytes)
-        : mCommands(commands), mFrameSizeBytes(frameSizeBytes) {
+    StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands, size_t frameSizeBytes,
+                             bool isMmap)
+        : mCommands(commands), mFrameSizeBytes(frameSizeBytes), mIsMmap(isMmap) {
         mCommands->rewind();
     }
 
-    // The three methods below is intended to be called after the worker
+    // The five methods below is intended to be called after the worker
     // thread has joined, thus no extra synchronization is needed.
     bool hasObservablePositionIncrease() const { return mObservablePositionIncrease; }
-    bool hasRetrogradeObservablePosition() const { return mRetrogradeObservablePosition; }
+    bool hasObservableRetrogradePosition() const { return mRetrogradeObservablePosition; }
+    bool hasHardwarePositionIncrease() const {
+        // For non-MMap, always return true to pass the validation.
+        return mIsMmap ? mHardwarePositionIncrease : true;
+    }
+    bool hasHardwareRetrogradePosition() const {
+        // For non-MMap, always return false to pass the validation.
+        return mIsMmap ? mRetrogradeHardwarePosition : false;
+    }
     std::string getUnexpectedStateTransition() const { return mUnexpectedTransition; }
 
     bool done() override { return mCommands->done(); }
@@ -2940,14 +3012,24 @@
     bool interceptRawReply(const StreamDescriptor::Reply&) override { return false; }
     bool processValidReply(const StreamDescriptor::Reply& reply) override {
         if (reply.observable.frames != StreamDescriptor::Position::UNKNOWN) {
-            if (mPreviousFrames.has_value()) {
-                if (reply.observable.frames > mPreviousFrames.value()) {
+            if (mPreviousObservableFrames.has_value()) {
+                if (reply.observable.frames > mPreviousObservableFrames.value()) {
                     mObservablePositionIncrease = true;
-                } else if (reply.observable.frames < mPreviousFrames.value()) {
+                } else if (reply.observable.frames < mPreviousObservableFrames.value()) {
                     mRetrogradeObservablePosition = true;
                 }
             }
-            mPreviousFrames = reply.observable.frames;
+            mPreviousObservableFrames = reply.observable.frames;
+        }
+        if (mIsMmap) {
+            if (mPreviousHardwareFrames.has_value()) {
+                if (reply.hardware.frames > mPreviousHardwareFrames.value()) {
+                    mHardwarePositionIncrease = true;
+                } else if (reply.hardware.frames < mPreviousHardwareFrames.value()) {
+                    mRetrogradeHardwarePosition = true;
+                }
+            }
+            mPreviousHardwareFrames = reply.hardware.frames;
         }
 
         auto expected = mCommands->getExpectedStates();
@@ -2974,10 +3056,14 @@
   protected:
     std::shared_ptr<StateSequence> mCommands;
     const size_t mFrameSizeBytes;
+    const bool mIsMmap;
     std::optional<StreamDescriptor::State> mPreviousState;
-    std::optional<int64_t> mPreviousFrames;
+    std::optional<int64_t> mPreviousObservableFrames;
     bool mObservablePositionIncrease = false;
     bool mRetrogradeObservablePosition = false;
+    std::optional<int64_t> mPreviousHardwareFrames;
+    bool mHardwarePositionIncrease = false;
+    bool mRetrogradeHardwarePosition = false;
     std::string mUnexpectedTransition;
 };
 
@@ -2988,8 +3074,8 @@
 static bool skipStreamIoTestForMixPortConfig(const AudioPortConfig& portConfig) {
     return (portConfig.flags.value().getTag() == AudioIoFlags::input &&
             isAnyBitPositionFlagSet(portConfig.flags.value().template get<AudioIoFlags::input>(),
-                                    {AudioInputFlags::MMAP_NOIRQ, AudioInputFlags::VOIP_TX,
-                                     AudioInputFlags::HW_HOTWORD, AudioInputFlags::HOTWORD_TAP})) ||
+                                    {AudioInputFlags::VOIP_TX, AudioInputFlags::HW_HOTWORD,
+                                     AudioInputFlags::HOTWORD_TAP})) ||
            (portConfig.flags.value().getTag() == AudioIoFlags::output &&
             isAnyBitPositionFlagSet(
                     portConfig.flags.value().template get<AudioIoFlags::output>(),
@@ -3029,8 +3115,8 @@
 
     void StartWorkerToSendBurstCommands() {
         const StreamContext* context = mStream->getStreamContext();
-        mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(makeBurstCommands(mIsSync),
-                                                                   context->getFrameSizeBytes());
+        mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(
+                makeBurstCommands(mIsSync), context->getFrameSizeBytes(), context->isMmapped());
         mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
                 *context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
         LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
@@ -3047,10 +3133,13 @@
         EXPECT_FALSE(mWorker->hasError()) << mWorker->getError();
         EXPECT_EQ("", mWorkerDriver->getUnexpectedStateTransition());
         if (validatePosition) {
-            if (IOTraits<Stream>::is_input) {
+            if (IOTraits<Stream>::is_input &&
+                !mStream->getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
                 EXPECT_TRUE(mWorkerDriver->hasObservablePositionIncrease());
+                EXPECT_TRUE(mWorkerDriver->hasHardwarePositionIncrease());
             }
-            EXPECT_FALSE(mWorkerDriver->hasRetrogradeObservablePosition());
+            EXPECT_FALSE(mWorkerDriver->hasObservableRetrogradePosition());
+            EXPECT_FALSE(mWorkerDriver->hasHardwareRetrogradePosition());
         }
         mWorker.reset();
         mWorkerDriver.reset();
@@ -3984,7 +4073,7 @@
         }
     }
 
-    bool ValidateObservablePosition(const AudioDevice& device) {
+    bool ValidatePosition(const AudioDevice& device) {
         return !isTelephonyDeviceType(device.type.type);
     }
 
@@ -3998,7 +4087,8 @@
         if (skipStreamIoTestForDevice(stream.getDevice())) return;
         ASSERT_EQ("", stream.skipTestReason());
         StreamLogicDefaultDriver driver(commandsAndStates,
-                                        stream.getStreamContext()->getFrameSizeBytes());
+                                        stream.getStreamContext()->getFrameSizeBytes(),
+                                        stream.getStreamContext()->isMmapped());
         typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
                                                  stream.getStreamEventReceiver());
 
@@ -4008,11 +4098,14 @@
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
-        if (ValidateObservablePosition(stream.getDevice())) {
-            if (validatePositionIncrease) {
+        if (ValidatePosition(stream.getDevice())) {
+            if (validatePositionIncrease &&
+                !stream.getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
                 EXPECT_TRUE(driver.hasObservablePositionIncrease());
+                EXPECT_TRUE(driver.hasHardwarePositionIncrease());
             }
-            EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+            EXPECT_FALSE(driver.hasObservableRetrogradePosition());
+            EXPECT_FALSE(driver.hasHardwareRetrogradePosition());
         }
     }
 
@@ -4028,7 +4121,8 @@
         ASSERT_EQ("", stream.skipTestReason());
         ASSERT_NO_FATAL_FAILURE(stream.TeardownPatchSetUpStream(module.get()));
         StreamLogicDefaultDriver driver(commandsAndStates,
-                                        stream.getStreamContext()->getFrameSizeBytes());
+                                        stream.getStreamContext()->getFrameSizeBytes(),
+                                        stream.getStreamContext()->isMmapped());
         typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
                                                  stream.getStreamEventReceiver());
         ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get()));
@@ -4039,11 +4133,14 @@
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
-        if (ValidateObservablePosition(stream.getDevice())) {
-            if (validatePositionIncrease) {
+        if (ValidatePosition(stream.getDevice())) {
+            if (validatePositionIncrease &&
+                !stream.getStreamContext()->isMmapped() /*TODO(b/274456992) remove*/) {
                 EXPECT_TRUE(driver.hasObservablePositionIncrease());
+                EXPECT_TRUE(driver.hasHardwarePositionIncrease());
             }
-            EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+            EXPECT_FALSE(driver.hasObservableRetrogradePosition());
+            EXPECT_FALSE(driver.hasHardwareRetrogradePosition());
         }
     }
 };