Merge "audio: fix mmap output" into main

This commit is contained in:
Treehugger Robot
2024-06-27 01:07:49 +00:00
committed by Gerrit Code Review
6 changed files with 231 additions and 84 deletions

View File

@@ -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 @@ 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

View File

@@ -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<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 @@ 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<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
std::vector<AudioRoute*> 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<StreamIn> 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<StreamOut> stream;
RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
in_args.offloadInfo, &stream));

View File

@@ -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<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
@@ -657,6 +674,7 @@ ndk::ScopedAStatus StreamCommonImpl::initInstance(
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();

View File

@@ -205,6 +205,9 @@ class Module : public BnModule {
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) {

View File

@@ -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 <class WorkerLogic>
@@ -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 {

View File

@@ -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<DataMQ> 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<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 @@ 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<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 @@ 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<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 @@ 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<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 @@ std::shared_ptr<StateSequence> makeBurstCommands(bool isSync);
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 @@ class StreamFixtureWithWorker {
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 @@ class StreamFixtureWithWorker {
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 @@ 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<Stream>::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<Stream>::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());
}
}
};