audio: Add stub default MMAP implementation
Add support for MMAP simulation to the default
implementation. Since there is no support for MMAP audio I/O on
CVD, the implementation is a stub.
The implementation supports 'createMmapBuffer' operation via
vendor property 'aosp.createMmapBuffer'. VTS tests for MMAP
streams are enabled back for this case (and for upcoming API
V4). VTS calls 'createMmapBuffer' after the stream has
transitioned out from the 'STANDBY' state.
Also, VTS for MMAP updated to wait for the burst size duration
after each 'burst' command in order to simulate audio I/O flow
and ensure that the stream positions get eventually updated.
Note that even with 'createMmapBuffer', the HAL implementation
must still provide some valid shared memory FD when opening an MMAP
stream. This is a limitation due to the requirement of the HAL
API on the descriptor being "non-null". The VTS and the framework
will not try to 'mmap' that initial FD.
Bug: 274456992
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I6d5783b3108886ff5c1328ddc60c623d4290d3d3
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 36fd0d0..a8e8c50 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -80,6 +80,7 @@
"stub/ApeHeader.cpp",
"stub/DriverStubImpl.cpp",
"stub/ModuleStub.cpp",
+ "stub/StreamMmapStub.cpp",
"stub/StreamOffloadStub.cpp",
"stub/StreamStub.cpp",
"usb/ModuleUsb.cpp",
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
index 2a1dba9..6cb9251 100644
--- a/audio/aidl/default/ModulePrimary.cpp
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -21,18 +21,23 @@
#include <android-base/logging.h>
#include "core-impl/ModulePrimary.h"
+#include "core-impl/StreamMmapStub.h"
#include "core-impl/StreamOffloadStub.h"
#include "core-impl/StreamPrimary.h"
#include "core-impl/Telephony.h"
using aidl::android::hardware::audio::common::areAllBitPositionFlagsSet;
+using aidl::android::hardware::audio::common::hasMmapFlag;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioOutputFlags;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
@@ -62,6 +67,11 @@
const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
+ if (context.isMmap()) {
+ // "Stub" is used because there is no support for MMAP audio I/O on CVD.
+ return createStreamInstance<StreamInMmapStub>(result, std::move(context), sinkMetadata,
+ microphones);
+ }
return createStreamInstance<StreamInPrimary>(result, std::move(context), sinkMetadata,
microphones);
}
@@ -69,26 +79,54 @@
ndk::ScopedAStatus ModulePrimary::createOutputStream(
StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- if (!areAllBitPositionFlagsSet(
- context.getFlags().get<AudioIoFlags::output>(),
- {AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::NON_BLOCKING})) {
- return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
- offloadInfo);
- } else {
+ if (context.isMmap()) {
+ // "Stub" is used because there is no support for MMAP audio I/O on CVD.
+ return createStreamInstance<StreamOutMmapStub>(result, std::move(context), sourceMetadata,
+ offloadInfo);
+ } else if (areAllBitPositionFlagsSet(
+ context.getFlags().get<AudioIoFlags::output>(),
+ {AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::NON_BLOCKING})) {
// "Stub" is used because there is no actual decoder. The stream just
// extracts the clip duration from the media file header and simulates
// playback over time.
return createStreamInstance<StreamOutOffloadStub>(result, std::move(context),
sourceMetadata, offloadInfo);
}
+ return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
+ offloadInfo);
}
-int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
+ndk::ScopedAStatus ModulePrimary::createMmapBuffer(const AudioPortConfig& portConfig,
+ int32_t bufferSizeFrames, int32_t frameSizeBytes,
+ MmapBufferDescriptor* desc) {
+ const size_t bufferSizeBytes = static_cast<size_t>(bufferSizeFrames) * frameSizeBytes;
+ // The actual mmap buffer for I/O is created after the stream exits standby, via
+ // 'IStreamCommon.createMmapBuffer'. But we must return a valid file descriptor here because
+ // 'MmapBufferDescriptor' can not contain a "null" fd.
+ const std::string regionName =
+ std::string("mmap-sim-o-") +
+ std::to_string(portConfig.ext.get<AudioPortExt::Tag::mix>().handle);
+ int fd = ashmem_create_region(regionName.c_str(), bufferSizeBytes);
+ if (fd < 0) {
+ PLOG(ERROR) << __func__ << ": failed to create shared memory region of " << bufferSizeBytes
+ << " bytes";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ desc->sharedMemory.fd = ndk::ScopedFileDescriptor(fd);
+ desc->sharedMemory.size = bufferSizeBytes;
+ desc->burstSizeFrames = bufferSizeFrames / 2;
+ desc->flags = 0;
+ LOG(DEBUG) << __func__ << ": " << desc->toString();
+ return ndk::ScopedAStatus::ok();
+}
+
+int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig& portConfig) {
+ static constexpr int32_t kLowLatencyMs = 5;
// 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows
// the virtual Android device implementation to pass CTS. Hardware implementations
// should have significantly lower latency.
- static constexpr int32_t kLatencyMs = 85;
- return kLatencyMs;
+ static constexpr int32_t kStandardLatencyMs = 85;
+ return hasMmapFlag(portConfig.flags.value()) ? kLowLatencyMs : kStandardLatencyMs;
}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
index a657dc5..c93deed 100644
--- a/audio/aidl/default/include/core-impl/ModulePrimary.h
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -42,6 +42,9 @@
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
+ ndk::ScopedAStatus createMmapBuffer(
+ const ::aidl::android::media::audio::common::AudioPortConfig& portConfig,
+ int32_t bufferSizeFrames, int32_t frameSizeBytes, MmapBufferDescriptor* desc) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
diff --git a/audio/aidl/default/include/core-impl/StreamMmapStub.h b/audio/aidl/default/include/core-impl/StreamMmapStub.h
new file mode 100644
index 0000000..0332007
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamMmapStub.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <string>
+
+#include "core-impl/DriverStubImpl.h"
+#include "core-impl/Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+namespace mmap {
+
+struct DspSimulatorState {
+ const bool isInput;
+ const int sampleRate;
+ const int frameSizeBytes;
+ const size_t bufferSizeBytes;
+ std::mutex lock;
+ // The lock is also used to prevent un-mapping while the memory is in use.
+ uint8_t* sharedMemory GUARDED_BY(lock) = nullptr;
+ StreamDescriptor::Position mmapPos GUARDED_BY(lock);
+};
+
+class DspSimulatorLogic : public ::android::hardware::audio::common::StreamLogic {
+ protected:
+ explicit DspSimulatorLogic(DspSimulatorState& sharedState) : mSharedState(sharedState) {}
+ std::string init() override;
+ Status cycle() override;
+
+ private:
+ DspSimulatorState& mSharedState;
+ uint32_t mCycleDurationUs = 0;
+ uint8_t* mMemBegin = nullptr;
+ uint8_t* mMemPos = nullptr;
+ int64_t mLastFrames = 0;
+};
+
+class DspSimulatorWorker
+ : public ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic> {
+ public:
+ explicit DspSimulatorWorker(DspSimulatorState& sharedState)
+ : ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic>(sharedState) {}
+};
+
+} // namespace mmap
+
+class DriverMmapStubImpl : public DriverStubImpl {
+ public:
+ explicit DriverMmapStubImpl(const StreamContext& context);
+ ::android::status_t init(DriverCallbackInterface* callback) override;
+ ::android::status_t drain(StreamDescriptor::DrainMode drainMode) override;
+ ::android::status_t pause() override;
+ ::android::status_t start() override;
+ ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+ int32_t* latencyMs) override;
+ void shutdown() override;
+ ::android::status_t refinePosition(StreamDescriptor::Position* position) override;
+ ::android::status_t getMmapPositionAndLatency(StreamDescriptor::Position* position,
+ int32_t* latency) override;
+
+ protected:
+ ::android::status_t initSharedMemory(int ashmemFd);
+
+ private:
+ ::android::status_t releaseSharedMemory() REQUIRES(mState.lock);
+ ::android::status_t startWorkerIfNeeded();
+
+ mmap::DspSimulatorState mState;
+ mmap::DspSimulatorWorker mDspWorker;
+ bool mDspWorkerStarted = false;
+};
+
+class StreamMmapStub : public StreamCommonImpl, public DriverMmapStubImpl {
+ public:
+ static const std::string kCreateMmapBufferName;
+
+ StreamMmapStub(StreamContext* context, const Metadata& metadata);
+ ~StreamMmapStub();
+
+ ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) override;
+ ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+ bool in_async) override;
+
+ private:
+ ndk::ScopedAStatus createMmapBuffer(MmapBufferDescriptor* desc);
+
+ ndk::ScopedFileDescriptor mSharedMemoryFd;
+};
+
+class StreamInMmapStub final : public StreamIn, public StreamMmapStub {
+ public:
+ friend class ndk::SharedRefBase;
+ StreamInMmapStub(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+};
+
+class StreamOutMmapStub final : public StreamOut, public StreamMmapStub {
+ public:
+ friend class ndk::SharedRefBase;
+ StreamOutMmapStub(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamOffloadStub.h b/audio/aidl/default/include/core-impl/StreamOffloadStub.h
index 24e98c2..09b88aa 100644
--- a/audio/aidl/default/include/core-impl/StreamOffloadStub.h
+++ b/audio/aidl/default/include/core-impl/StreamOffloadStub.h
@@ -19,12 +19,15 @@
#include <mutex>
#include <set>
#include <string>
+#include <vector>
#include "core-impl/DriverStubImpl.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
+namespace offload {
+
struct DspSimulatorState {
static constexpr int64_t kSkipBufferNotifyFrames = -1;
@@ -55,9 +58,11 @@
: ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic>(sharedState) {}
};
+} // namespace offload
+
class DriverOffloadStubImpl : public DriverStubImpl {
public:
- DriverOffloadStubImpl(const StreamContext& context);
+ explicit DriverOffloadStubImpl(const StreamContext& context);
::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode drainMode) override;
::android::status_t flush() override;
@@ -71,8 +76,8 @@
::android::status_t startWorkerIfNeeded();
const int64_t mBufferNotifyFrames;
- DspSimulatorState mState;
- DspSimulatorWorker mDspWorker;
+ offload::DspSimulatorState mState;
+ offload::DspSimulatorWorker mDspWorker;
bool mDspWorkerStarted = false;
};
diff --git a/audio/aidl/default/stub/DriverStubImpl.cpp b/audio/aidl/default/stub/DriverStubImpl.cpp
index 0d129e6..cb8ee70 100644
--- a/audio/aidl/default/stub/DriverStubImpl.cpp
+++ b/audio/aidl/default/stub/DriverStubImpl.cpp
@@ -15,6 +15,7 @@
*/
#include <cmath>
+#include <cstdlib>
#define LOG_TAG "AHAL_Stream"
#include <android-base/logging.h>
diff --git a/audio/aidl/default/stub/StreamMmapStub.cpp b/audio/aidl/default/stub/StreamMmapStub.cpp
new file mode 100644
index 0000000..f48aea4
--- /dev/null
+++ b/audio/aidl/default/stub/StreamMmapStub.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <cstdlib>
+
+#define LOG_TAG "AHAL_MmapStream"
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
+#include <error/Result.h>
+#include <utils/SystemClock.h>
+
+#include "core-impl/StreamMmapStub.h"
+
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+namespace mmap {
+
+std::string DspSimulatorLogic::init() {
+ {
+ std::lock_guard l(mSharedState.lock);
+ mSharedState.mmapPos.timeNs = StreamDescriptor::Position::UNKNOWN;
+ mSharedState.mmapPos.frames = StreamDescriptor::Position::UNKNOWN;
+ }
+ // Progress in buffer size chunks to make sure that VTS tolerates infrequent position updates
+ // (see b/350998390).
+ mCycleDurationUs = (mSharedState.bufferSizeBytes / mSharedState.frameSizeBytes) *
+ MICROS_PER_SECOND / mSharedState.sampleRate;
+ return "";
+}
+
+DspSimulatorLogic::Status DspSimulatorLogic::cycle() {
+ // Simulate DSP moving along in real time.
+ const int64_t timeBeginNs = ::android::uptimeNanos();
+ usleep(mCycleDurationUs);
+ int64_t newFrames;
+ std::lock_guard l(mSharedState.lock);
+ if (mMemBegin != mSharedState.sharedMemory) {
+ mMemBegin = mSharedState.sharedMemory;
+ if (mMemBegin != nullptr) mMemPos = mMemBegin;
+ }
+ if (mMemBegin != nullptr) {
+ mSharedState.mmapPos.timeNs = ::android::uptimeNanos();
+ newFrames = (mSharedState.mmapPos.timeNs - timeBeginNs) * mSharedState.sampleRate /
+ NANOS_PER_SECOND;
+ // Restore the reported frames position to ensure continuity.
+ if (mSharedState.mmapPos.frames == StreamDescriptor::Position::UNKNOWN) {
+ mSharedState.mmapPos.frames = mLastFrames;
+ }
+ mSharedState.mmapPos.frames += newFrames;
+ mLastFrames = mSharedState.mmapPos.frames;
+ if (mSharedState.isInput) {
+ for (size_t i = 0; i < static_cast<size_t>(newFrames) * mSharedState.frameSizeBytes;
+ ++i) {
+ *mMemPos++ = std::rand() % 255;
+ if (mMemPos >= mMemBegin + mSharedState.bufferSizeBytes) mMemPos = mMemBegin;
+ }
+ }
+ } else {
+ LOG(WARNING) << "No shared memory but the DSP is active";
+ mSharedState.mmapPos.timeNs = StreamDescriptor::Position::UNKNOWN;
+ mSharedState.mmapPos.frames = StreamDescriptor::Position::UNKNOWN;
+ }
+ return Status::CONTINUE;
+}
+
+} // namespace mmap
+
+using mmap::DspSimulatorState;
+
+DriverMmapStubImpl::DriverMmapStubImpl(const StreamContext& context)
+ : DriverStubImpl(context, 0 /*asyncSleepTimeUs*/),
+ mState{mIsInput, mSampleRate, static_cast<int>(mFrameSizeBytes),
+ mBufferSizeFrames * mFrameSizeBytes},
+ mDspWorker(mState) {
+ LOG_IF(FATAL, !context.isMmap()) << "The steam must be used in MMAP mode";
+}
+
+::android::status_t DriverMmapStubImpl::init(DriverCallbackInterface* callback) {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::init(callback));
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::drain(StreamDescriptor::DrainMode drainMode) {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::drain(drainMode));
+ mDspWorker.pause();
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::pause() {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::pause());
+ mDspWorker.pause();
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::start() {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::start());
+ RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
+ mDspWorker.resume();
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::transfer(void*, size_t, size_t*, int32_t*) {
+ // Do not call into DriverStubImpl::transfer
+ if (!mIsInitialized) {
+ LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+ }
+ if (mIsStandby) {
+ LOG(FATAL) << __func__ << ": must not happen while in standby";
+ }
+ RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
+ mDspWorker.resume();
+ return ::android::OK;
+}
+
+void DriverMmapStubImpl::shutdown() {
+ LOG(DEBUG) << __func__ << ": stopping the DSP simulator worker";
+ mDspWorker.stop();
+ std::lock_guard l(mState.lock);
+ releaseSharedMemory();
+ DriverStubImpl::shutdown();
+}
+
+::android::status_t DriverMmapStubImpl::initSharedMemory(int ashmemFd) {
+ {
+ std::lock_guard l(mState.lock);
+ if (ashmemFd == -1) {
+ mState.sharedMemory = nullptr;
+ return ::android::BAD_VALUE;
+ }
+ RETURN_STATUS_IF_ERROR(releaseSharedMemory());
+ }
+ uint8_t* sharedMemory = static_cast<uint8_t*>(::mmap(
+ nullptr, mState.bufferSizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
+ if (sharedMemory == reinterpret_cast<uint8_t*>(MAP_FAILED) || sharedMemory == nullptr) {
+ PLOG(ERROR) << "mmap failed for size " << mState.bufferSizeBytes << ", fd " << ashmemFd;
+ return ::android::NO_INIT;
+ }
+ std::lock_guard l(mState.lock);
+ mState.sharedMemory = sharedMemory;
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::releaseSharedMemory() {
+ if (mState.sharedMemory != nullptr) {
+ LOG(DEBUG) << __func__ << ": unmapping shared memory";
+ if (munmap(mState.sharedMemory, mState.bufferSizeBytes) != 0) {
+ PLOG(ERROR) << "munmap failed for size " << mState.bufferSizeBytes;
+ return ::android::INVALID_OPERATION;
+ }
+ mState.sharedMemory = nullptr;
+ }
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::startWorkerIfNeeded() {
+ if (!mDspWorkerStarted) {
+ // This is an "audio service thread," must have elevated priority.
+ if (!mDspWorker.start("dsp_sim", ANDROID_PRIORITY_URGENT_AUDIO)) {
+ return ::android::NO_INIT;
+ }
+ mDspWorkerStarted = true;
+ }
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::refinePosition(StreamDescriptor::Position* position) {
+ std::lock_guard l(mState.lock);
+ *position = mState.mmapPos;
+ return ::android::OK;
+}
+
+::android::status_t DriverMmapStubImpl::getMmapPositionAndLatency(
+ StreamDescriptor::Position* position, int32_t* latencyMs) {
+ {
+ std::lock_guard l(mState.lock);
+ *position = mState.mmapPos;
+ }
+ const size_t latencyFrames = mBufferSizeFrames / 2;
+ if (position->frames != StreamDescriptor::Position::UNKNOWN) {
+ position->frames += latencyFrames;
+ }
+ *latencyMs = latencyFrames * MILLIS_PER_SECOND / mSampleRate;
+ return ::android::OK;
+}
+
+const std::string StreamMmapStub::kCreateMmapBufferName = "aosp.createMmapBuffer";
+
+StreamMmapStub::StreamMmapStub(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata), DriverMmapStubImpl(getContext()) {}
+
+StreamMmapStub::~StreamMmapStub() {
+ cleanupWorker();
+}
+
+ndk::ScopedAStatus StreamMmapStub::getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) {
+ std::vector<std::string> unprocessedIds;
+ for (const auto& id : in_ids) {
+ if (id == kCreateMmapBufferName) {
+ LOG(DEBUG) << __func__ << ": " << id;
+ MmapBufferDescriptor mmapDesc;
+ RETURN_STATUS_IF_ERROR(createMmapBuffer(&mmapDesc));
+ VendorParameter createMmapBuffer{.id = id};
+ createMmapBuffer.ext.setParcelable(mmapDesc);
+ LOG(DEBUG) << __func__ << ": returning " << mmapDesc.toString();
+ _aidl_return->push_back(std::move(createMmapBuffer));
+ } else {
+ unprocessedIds.push_back(id);
+ }
+ }
+ if (!unprocessedIds.empty()) {
+ return StreamCommonImpl::getVendorParameters(unprocessedIds, _aidl_return);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamMmapStub::setVendorParameters(
+ const std::vector<VendorParameter>& in_parameters, bool in_async) {
+ std::vector<VendorParameter> unprocessedParameters;
+ for (const auto& param : in_parameters) {
+ if (param.id == kCreateMmapBufferName) {
+ LOG(DEBUG) << __func__ << ": " << param.id;
+ // The value is irrelevant. The fact that this parameter can be "set" is an
+ // indication that the method can be used by the client via 'getVendorParameters'.
+ } else {
+ unprocessedParameters.push_back(param);
+ }
+ }
+ if (!unprocessedParameters.empty()) {
+ return StreamCommonImpl::setVendorParameters(unprocessedParameters, in_async);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamMmapStub::createMmapBuffer(MmapBufferDescriptor* desc) {
+ const size_t bufferSizeFrames = mContext.getBufferSizeInFrames();
+ const size_t bufferSizeBytes = static_cast<size_t>(bufferSizeFrames) * mContext.getFrameSize();
+ const std::string regionName =
+ std::string("mmap-sim-") + std::to_string(mContext.getMixPortHandle());
+ int fd = ashmem_create_region(regionName.c_str(), bufferSizeBytes);
+ if (fd < 0) {
+ PLOG(ERROR) << __func__ << ": failed to create shared memory region of " << bufferSizeBytes
+ << " bytes";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mSharedMemoryFd = ndk::ScopedFileDescriptor(fd);
+ if (initSharedMemory(mSharedMemoryFd.get()) != ::android::OK) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ desc->sharedMemory.fd = mSharedMemoryFd.dup();
+ desc->sharedMemory.size = bufferSizeBytes;
+ desc->burstSizeFrames = bufferSizeFrames / 2;
+ desc->flags = 0;
+ LOG(DEBUG) << __func__ << ": " << desc->toString();
+ return ndk::ScopedAStatus::ok();
+}
+
+StreamInMmapStub::StreamInMmapStub(StreamContext&& context, const SinkMetadata& sinkMetadata,
+ const std::vector<MicrophoneInfo>& microphones)
+ : StreamIn(std::move(context), microphones), StreamMmapStub(&mContextInstance, sinkMetadata) {}
+
+StreamOutMmapStub::StreamOutMmapStub(StreamContext&& context, const SourceMetadata& sourceMetadata,
+ const std::optional<AudioOffloadInfo>& offloadInfo)
+ : StreamOut(std::move(context), offloadInfo),
+ StreamMmapStub(&mContextInstance, sourceMetadata) {}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/stub/StreamOffloadStub.cpp b/audio/aidl/default/stub/StreamOffloadStub.cpp
index 155f76d..5f5f741 100644
--- a/audio/aidl/default/stub/StreamOffloadStub.cpp
+++ b/audio/aidl/default/stub/StreamOffloadStub.cpp
@@ -30,6 +30,8 @@
namespace aidl::android::hardware::audio::core {
+namespace offload {
+
std::string DspSimulatorLogic::init() {
return "";
}
@@ -90,6 +92,10 @@
return Status::CONTINUE;
}
+} // namespace offload
+
+using offload::DspSimulatorState;
+
DriverOffloadStubImpl::DriverOffloadStubImpl(const StreamContext& context)
: DriverStubImpl(context, 0 /*asyncSleepTimeUs*/),
mBufferNotifyFrames(static_cast<int64_t>(context.getBufferSizeInFrames()) / 2),