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()); } } };