From 53a8d4d62ec08f410256b98901509b24b7fab583 Mon Sep 17 00:00:00 2001 From: Kuowei Li Date: Mon, 24 Jun 2024 14:35:07 +0800 Subject: [PATCH] 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 --- audio/aidl/common/include/Utils.h | 12 ++ audio/aidl/default/Module.cpp | 66 ++++--- audio/aidl/default/Stream.cpp | 54 ++++-- audio/aidl/default/include/core-impl/Module.h | 3 + audio/aidl/default/include/core-impl/Stream.h | 7 + .../vts/VtsHalAudioCoreModuleTargetTest.cpp | 173 ++++++++++++++---- 6 files changed, 231 insertions(+), 84 deletions(-) diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h index a1008a4d89..dd216e59ef 100644 --- a/audio/aidl/common/include/Utils.h +++ b/audio/aidl/common/include/Utils.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -188,4 +189,15 @@ constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRat 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 0d6151e65b..543efd1b3e 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 @@ ndk::ScopedAStatus Module::createStreamContext( return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } const auto& flags = portConfigIt->flags.value(); - if ((flags.getTag() == AudioIoFlags::Tag::input && - !isBitPositionFlagSet(flags.get(), - AudioInputFlags::MMAP_NOIRQ)) || - (flags.getTag() == AudioIoFlags::Tag::output && - !isBitPositionFlagSet(flags.get(), - AudioOutputFlags::MMAP_NOIRQ))) { - StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs, - mVendorDebug.forceTransientBurst, - mVendorDebug.forceSynchronousDrain}; - std::shared_ptr 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(1, true /*configureEventFlagWord*/), - std::make_unique(1, true /*configureEventFlagWord*/), - portConfigIt->format.value(), portConfigIt->channelMask.value(), - portConfigIt->sampleRate.value().value, flags, nominalLatencyMs, - portConfigIt->ext.get().handle, - std::make_unique(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 dataMQ = nullptr; + std::shared_ptr streamAsyncCallback = nullptr; + std::shared_ptr 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(frameSize * in_bufferSizeFrames); + streamAsyncCallback = asyncCallback; + } + StreamContext temp( + std::make_unique(1, true /*configureEventFlagWord*/), + std::make_unique(1, true /*configureEventFlagWord*/), + portConfigIt->format.value(), portConfigIt->channelMask.value(), + portConfigIt->sampleRate.value().value, flags, nominalLatencyMs, + portConfigIt->ext.get().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 @@ int32_t Module::getNominalLatencyMs(const AudioPortConfig&) { 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 Module::getAudioRoutesForAudioPortImpl(int32_t portId) { std::vector result; auto& routes = getConfig().routes; @@ -866,6 +870,9 @@ ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_ar 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 stream; RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata, getMicrophoneInfos(), &stream)); @@ -913,6 +920,9 @@ ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_ 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 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 31b064590d..8f5e83914c 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 @@ bool StreamContext::isValid() const { 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 @@ pid_t StreamWorkerCommonLogic::getTid() const { 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 @@ std::string StreamWorkerCommonLogic::init() { 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 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() { 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 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() { 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 asyncCallback = mContext->getAsyncCallback(); @@ -657,6 +674,7 @@ ndk::ScopedAStatus StreamCommonImpl::initInstance( const std::shared_ptr& delegate) { mCommon = ndk::SharedRefBase::make(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 a326217904..00eeb4ee20 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -205,6 +205,9 @@ class Module : public BnModule { virtual std::unique_ptr 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 21e63f9fef..6b45866a38 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -177,6 +177,11 @@ struct DriverInterface { 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 @@ struct StreamWorkerInterface { virtual bool start() = 0; virtual pid_t getTid() = 0; virtual void stop() = 0; + virtual std::string getError() = 0; }; template @@ -260,6 +266,7 @@ class StreamWorkerImpl : public StreamWorkerInterface, } 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 d576c7c826..bbc4caf79e 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 @@ class StreamContext { 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 @@ class StreamContext { 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 maybeCreateDataMQ(const StreamDescriptor& descriptor) { @@ -667,12 +690,26 @@ class StreamContext { } 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().sharedMemory.fd.get(); + } + return -1; + } const size_t mFrameSizeBytes; std::unique_ptr mCommandMQ; std::unique_ptr mReplyMQ; const size_t mBufferSizeFrames; std::unique_ptr mDataMQ; + const bool mIsMmapped; + const int32_t mSharedMemoryFd; + int8_t* mSharedMemory = nullptr; }; struct StreamEventReceiver { @@ -868,12 +905,15 @@ class StreamCommonLogic : public StreamLogic { 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 @@ class StreamCommonLogic : public StreamLogic { 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 @@ class StreamCommonLogic : public StreamLogic { 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 @@ class StreamReaderLogic : public StreamCommonLogic { << ": received invalid byte count in the reply: " << reply.fmqByteCount; return Status::ABORT; } - if (static_cast(reply.fmqByteCount) != getDataMQ()->availableToRead()) { + if (!isMmapped() && + static_cast(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 @@ class StreamReaderLogic : public StreamCommonLogic { 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 @@ class StreamWriterLogic : public StreamCommonLogic { 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 @@ class StreamWriterLogic : public StreamCommonLogic { 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 StreamFixture { class StreamLogicDefaultDriver : public StreamLogicDriver { public: - StreamLogicDefaultDriver(std::shared_ptr commands, size_t frameSizeBytes) - : mCommands(commands), mFrameSizeBytes(frameSizeBytes) { + StreamLogicDefaultDriver(std::shared_ptr 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 @@ class StreamLogicDefaultDriver : public StreamLogicDriver { 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 @@ class StreamLogicDefaultDriver : public StreamLogicDriver { protected: std::shared_ptr mCommands; const size_t mFrameSizeBytes; + const bool mIsMmap; std::optional mPreviousState; - std::optional mPreviousFrames; + std::optional mPreviousObservableFrames; bool mObservablePositionIncrease = false; bool mRetrogradeObservablePosition = false; + std::optional mPreviousHardwareFrames; + bool mHardwarePositionIncrease = false; + bool mRetrogradeHardwarePosition = false; std::string mUnexpectedTransition; }; @@ -2988,8 +3074,8 @@ std::shared_ptr makeBurstCommands(bool isSync); static bool skipStreamIoTestForMixPortConfig(const AudioPortConfig& portConfig) { return (portConfig.flags.value().getTag() == AudioIoFlags::input && isAnyBitPositionFlagSet(portConfig.flags.value().template get(), - {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(), @@ -3029,8 +3115,8 @@ class StreamFixtureWithWorker { void StartWorkerToSendBurstCommands() { const StreamContext* context = mStream->getStreamContext(); - mWorkerDriver = std::make_unique(makeBurstCommands(mIsSync), - context->getFrameSizeBytes()); + mWorkerDriver = std::make_unique( + makeBurstCommands(mIsSync), context->getFrameSizeBytes(), context->isMmapped()); mWorker = std::make_unique::Worker>( *context, mWorkerDriver.get(), mStream->getStreamEventReceiver()); LOG(DEBUG) << __func__ << ": starting " << IOTraits::directionStr << " worker..."; @@ -3047,10 +3133,13 @@ class StreamFixtureWithWorker { EXPECT_FALSE(mWorker->hasError()) << mWorker->getError(); EXPECT_EQ("", mWorkerDriver->getUnexpectedStateTransition()); if (validatePosition) { - if (IOTraits::is_input) { + if (IOTraits::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 @@ class AudioStreamIo : public AudioCoreModuleBase, } } - bool ValidateObservablePosition(const AudioDevice& device) { + bool ValidatePosition(const AudioDevice& device) { return !isTelephonyDeviceType(device.type.type); } @@ -3998,7 +4087,8 @@ class AudioStreamIo : public AudioCoreModuleBase, if (skipStreamIoTestForDevice(stream.getDevice())) return; ASSERT_EQ("", stream.skipTestReason()); StreamLogicDefaultDriver driver(commandsAndStates, - stream.getStreamContext()->getFrameSizeBytes()); + stream.getStreamContext()->getFrameSizeBytes(), + stream.getStreamContext()->isMmapped()); typename IOTraits::Worker worker(*stream.getStreamContext(), &driver, stream.getStreamEventReceiver()); @@ -4008,11 +4098,14 @@ class AudioStreamIo : public AudioCoreModuleBase, 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 @@ class AudioStreamIo : public AudioCoreModuleBase, 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::Worker worker(*stream.getStreamContext(), &driver, stream.getStreamEventReceiver()); ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get())); @@ -4039,11 +4133,14 @@ class AudioStreamIo : public AudioCoreModuleBase, 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()); } } };