Snap for 13241370 from a7f987dd4e53b3a62b10d62fef3a2b14bd7d29f7 to 25Q2-release
Change-Id: I8f78d679e259633cbe0c568f3cc63e69ef77e793
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 230c717..a9ecdc2 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),
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 806c93f..2c692f5 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -27,6 +27,7 @@
#include <set>
#include <string>
#include <string_view>
+#include <thread>
#include <variant>
#include <vector>
@@ -76,6 +77,7 @@
using aidl::android::hardware::audio::core::IStreamIn;
using aidl::android::hardware::audio::core::IStreamOut;
using aidl::android::hardware::audio::core::ITelephony;
+using aidl::android::hardware::audio::core::MmapBufferDescriptor;
using aidl::android::hardware::audio::core::ModuleDebug;
using aidl::android::hardware::audio::core::StreamDescriptor;
using aidl::android::hardware::audio::core::VendorParameter;
@@ -720,21 +722,8 @@
mFlags(flags),
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());
- }
- }
+ mMmapBurstSizeFrames(getMmapBurstSizeFrames(descriptor)),
+ mSharedMemoryFd(maybeGetMmapFd(descriptor)) {}
void checkIsValid() const {
EXPECT_NE(0UL, mFrameSizeBytes);
ASSERT_NE(nullptr, mCommandMQ);
@@ -742,15 +731,14 @@
ASSERT_NE(nullptr, mReplyMQ);
EXPECT_TRUE(mReplyMQ->isValid());
if (isMmapped()) {
- ASSERT_NE(nullptr, mSharedMemory);
+ EXPECT_NE(0, mMmapBurstSizeFrames) << "MMAP burst size must not be zero";
} 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";
- }
+ ASSERT_NE(nullptr, mDataMQ);
+ 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; }
@@ -763,7 +751,8 @@
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getSampleRate() const { return mConfig.sampleRate; }
bool isMmapped() const { return mIsMmapped; }
- int8_t* getMmapMemory() const { return mSharedMemory; }
+ int32_t getMmapBurstSizeFrames() const { return mMmapBurstSizeFrames; }
+ int getMmapFd() const { return mSharedMemoryFd; }
private:
static std::unique_ptr<DataMQ> maybeCreateDataMQ(const StreamDescriptor& descriptor) {
@@ -773,6 +762,13 @@
}
return nullptr;
}
+ static int32_t getMmapBurstSizeFrames(const StreamDescriptor& descriptor) {
+ using Tag = StreamDescriptor::AudioBuffer::Tag;
+ if (descriptor.audio.getTag() == Tag::mmap) {
+ return descriptor.audio.get<Tag::mmap>().burstSizeFrames;
+ }
+ return -1;
+ }
static bool isMmapped(const StreamDescriptor& descriptor) {
using Tag = StreamDescriptor::AudioBuffer::Tag;
return descriptor.audio.getTag() == Tag::mmap;
@@ -793,8 +789,75 @@
const AudioIoFlags mFlags;
std::unique_ptr<DataMQ> mDataMQ;
const bool mIsMmapped;
- const int32_t mSharedMemoryFd;
- int8_t* mSharedMemory = nullptr;
+ const int32_t mMmapBurstSizeFrames;
+ const int32_t mSharedMemoryFd; // owned by StreamDescriptor
+};
+
+struct StreamWorkerMethods {
+ virtual ~StreamWorkerMethods() = default;
+ virtual bool createMmapBuffer(MmapBufferDescriptor* desc) = 0;
+ virtual bool supportsCreateMmapBuffer() = 0;
+};
+
+class MmapSharedMemory {
+ public:
+ explicit MmapSharedMemory(const StreamContext& context, StreamWorkerMethods* stream)
+ : mStream(stream),
+ mBufferSizeBytes(context.getBufferSizeBytes()),
+ mSharedMemoryFd(::dup(context.getMmapFd())) {}
+ ~MmapSharedMemory() { releaseSharedMemory(); }
+
+ int8_t* getMmapMemory() {
+ if (mSharedMemory != nullptr) return mSharedMemory;
+ if (mSharedMemoryFd.get() != -1) {
+ int8_t* sharedMemory = (int8_t*)mmap(nullptr, mBufferSizeBytes, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mSharedMemoryFd.get(), 0);
+ if (sharedMemory != MAP_FAILED && sharedMemory != nullptr) {
+ mSharedMemory = sharedMemory;
+ } else {
+ PLOG(ERROR) << __func__ << ": mmap() failed, fd " << mSharedMemoryFd.get()
+ << ", size " << mBufferSizeBytes;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": shared memory FD has not been set yet";
+ }
+ return mSharedMemory;
+ }
+ bool updateMmapSharedMemoryIfNeeded(StreamDescriptor::State state) {
+ if (mPreviousState == StreamDescriptor::State::STANDBY &&
+ state != StreamDescriptor::State::STANDBY && state != StreamDescriptor::State::ERROR) {
+ LOG(INFO) << "Mmap stream exited standby, update Mmap buffer";
+ MmapBufferDescriptor desc;
+ if (!mStream->createMmapBuffer(&desc)) return false;
+ updateMmapSharedMemoryFd(desc);
+ }
+ mPreviousState = state;
+ return true;
+ }
+
+ private:
+ static ndk::ScopedFileDescriptor getMmapFd(const MmapBufferDescriptor& desc) {
+ return desc.sharedMemory.fd.get() != -1 ? desc.sharedMemory.fd.dup()
+ : ndk::ScopedFileDescriptor{};
+ }
+ void releaseSharedMemory() {
+ if (mSharedMemory != nullptr) {
+ munmap(mSharedMemory, mBufferSizeBytes);
+ }
+ mSharedMemory = nullptr;
+ }
+ void updateMmapSharedMemoryFd(const MmapBufferDescriptor& desc) {
+ mSharedMemoryFd = getMmapFd(desc);
+ releaseSharedMemory();
+ }
+
+ StreamWorkerMethods* const mStream;
+ const size_t mBufferSizeBytes;
+ ndk::ScopedFileDescriptor mSharedMemoryFd;
+ // Maps on the worker thread, may unmap in the destructor on the main thread.
+ std::atomic<int8_t*> mSharedMemory = nullptr;
+ // 'STANDBY' is always the starting state for a stream.
+ StreamDescriptor::State mPreviousState = StreamDescriptor::State::STANDBY;
};
struct StreamEventReceiver {
@@ -984,15 +1047,18 @@
class StreamCommonLogic : public StreamLogic {
protected:
StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver,
- StreamEventReceiver* eventReceiver)
+ StreamWorkerMethods* stream, StreamEventReceiver* eventReceiver)
: mCommandMQ(context.getCommandMQ()),
mReplyMQ(context.getReplyMQ()),
mDataMQ(context.getDataMQ()),
+ mMmap(context, stream),
mData(context.getBufferSizeBytes()),
mDriver(driver),
mEventReceiver(eventReceiver),
mIsMmapped(context.isMmapped()),
- mSharedMemory(context.getMmapMemory()),
+ mMmapBurstSleep(mIsMmapped ? static_cast<double>(context.getMmapBurstSizeFrames()) /
+ context.getSampleRate()
+ : 0.0),
mIsCompressOffload(context.getFlags().getTag() == AudioIoFlags::output &&
isBitPositionFlagSet(context.getFlags().get<AudioIoFlags::output>(),
AudioOutputFlags::COMPRESS_OFFLOAD)),
@@ -1008,7 +1074,9 @@
bool isMmapped() const { return mIsMmapped; }
std::string init() override {
- LOG(DEBUG) << __func__;
+ LOG(DEBUG) << __func__ << ": isMmapped? " << mIsMmapped << ", MmapBurstSleep "
+ << mMmapBurstSleep << ", isCompressOffload? " << mIsCompressOffload << ", "
+ << mConfig.toString();
return "";
}
const std::vector<int8_t>& getData() const { return mData; }
@@ -1054,32 +1122,40 @@
return false;
}
bool readDataFromMmap(size_t readCount) {
- if (mSharedMemory != nullptr) {
- std::memcpy(mData.data(), mSharedMemory, readCount);
+ if (auto memory = mMmap.getMmapMemory(); memory != nullptr) {
+ std::memcpy(mData.data(), memory, readCount);
+ // Since MMap `burst` does not block, need to sleep here to get an updated position.
+ std::this_thread::sleep_for(mMmapBurstSleep);
return true;
}
- LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from mmap failed";
+ LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from MMap failed";
return false;
}
bool writeDataToMmap() {
- if (mSharedMemory != nullptr) {
- std::memcpy(mSharedMemory, mData.data(), mData.size());
+ if (auto memory = mMmap.getMmapMemory(); memory != nullptr) {
+ std::memcpy(memory, mData.data(), mData.size());
+ // Since MMap `burst` does not block, need to sleep here to get an updated position.
+ std::this_thread::sleep_for(mMmapBurstSleep);
return true;
}
- LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to mmap failed";
+ LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MMap failed";
return false;
}
+ bool updateMmapSharedMemoryIfNeeded(StreamDescriptor::State state) {
+ return isMmapped() ? mMmap.updateMmapSharedMemoryIfNeeded(state) : true;
+ }
private:
StreamContext::CommandMQ* mCommandMQ;
StreamContext::ReplyMQ* mReplyMQ;
StreamContext::DataMQ* mDataMQ;
+ MmapSharedMemory mMmap;
std::vector<int8_t> mData;
StreamLogicDriver* const mDriver;
StreamEventReceiver* const mEventReceiver;
int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
const bool mIsMmapped;
- int8_t* mSharedMemory = nullptr;
+ const std::chrono::duration<double> mMmapBurstSleep;
const bool mIsCompressOffload;
const AudioConfigBase mConfig;
};
@@ -1087,8 +1163,9 @@
class StreamReaderLogic : public StreamCommonLogic {
public:
StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver,
- StreamEventReceiver* eventReceiver)
- : StreamCommonLogic(context, driver, eventReceiver) {}
+ StreamWorkerMethods* stream, StreamEventReceiver* eventReceiver)
+ : StreamCommonLogic(context, driver, stream, eventReceiver),
+ mMmapBurstSizeFrames(context.getMmapBurstSizeFrames()) {}
// Should only be called after the worker has joined.
const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
@@ -1154,30 +1231,35 @@
}
const bool acceptedReply = getDriver()->processValidReply(reply);
if (const size_t readCount =
- !isMmapped() ? getDataMQ()->availableToRead() : reply.fmqByteCount;
+ !isMmapped() ? getDataMQ()->availableToRead()
+ : (command.getTag() == StreamDescriptor::Command::Tag::burst
+ ? mMmapBurstSizeFrames
+ : 0);
readCount > 0) {
fillData(-1);
if (isMmapped() ? readDataFromMmap(readCount) : readDataFromMQ(readCount)) {
goto checkAcceptedReply;
}
- LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+ LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes failed";
return Status::ABORT;
} // readCount == 0
checkAcceptedReply:
if (acceptedReply) {
- return Status::CONTINUE;
+ return updateMmapSharedMemoryIfNeeded(reply.state) ? Status::CONTINUE : Status::ABORT;
}
LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
return Status::ABORT;
}
+
+ const int32_t mMmapBurstSizeFrames;
};
using StreamReader = StreamWorker<StreamReaderLogic>;
class StreamWriterLogic : public StreamCommonLogic {
public:
StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver,
- StreamEventReceiver* eventReceiver)
- : StreamCommonLogic(context, driver, eventReceiver) {}
+ StreamWorkerMethods* stream, StreamEventReceiver* eventReceiver)
+ : StreamCommonLogic(context, driver, stream, eventReceiver) {}
// Should only be called after the worker has joined.
const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
@@ -1293,7 +1375,7 @@
return Status::ABORT;
}
if (getDriver()->processValidReply(reply)) {
- return Status::CONTINUE;
+ return updateMmapSharedMemoryIfNeeded(reply.state) ? Status::CONTINUE : Status::ABORT;
}
LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
return Status::ABORT;
@@ -1381,7 +1463,7 @@
};
template <typename Stream>
-class WithStream {
+class WithStream : public StreamWorkerMethods {
public:
static ndk::ScopedAStatus callClose(std::shared_ptr<Stream> stream) {
std::shared_ptr<IStreamCommon> common;
@@ -1421,6 +1503,7 @@
const AudioConfigBase cfg{config.sampleRate->value, *config.channelMask, *config.format};
mContext.emplace(mDescriptor, cfg, config.flags.value());
ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
+ ASSERT_IS_OK(mStream->getInterfaceVersion(&mInterfaceVersion));
}
void SetUp(IModule* module, long bufferSizeFrames) {
ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module));
@@ -1432,13 +1515,66 @@
std::shared_ptr<Stream> getSharedPointer() const { return mStream; }
const AudioPortConfig& getPortConfig() const { return mPortConfig.get(); }
int32_t getPortId() const { return mPortConfig.getId(); }
+ // StreamWorkerMethods
+ bool createMmapBuffer(MmapBufferDescriptor* desc) override {
+ std::shared_ptr<IStreamCommon> common;
+ ndk::ScopedAStatus status = mStream->getStreamCommon(&common);
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": getStreamCommon failed: " << status.getMessage();
+ return false;
+ }
+ if (mInterfaceVersion <= kAidlVersion3) {
+ std::vector<VendorParameter> parameters;
+ ScopedAStatus result = common->getVendorParameters({kCreateMmapBuffer}, ¶meters);
+ if (result.isOk() && parameters.size() == 1) {
+ std::optional<MmapBufferDescriptor> result;
+ binder_status_t status = parameters[0].ext.getParcelable(&result);
+ if (status == ::android::OK) {
+ *desc = std::move(*result);
+ return true;
+ } else {
+ LOG(ERROR) << __func__ << ": failed to extract parcelable: " << status;
+ }
+ } else {
+ LOG(ERROR) << __func__
+ << ": failed to call 'createMmapBuffer' via 'getVendorParameter': "
+ << result.getMessage();
+ }
+ } else {
+ // TODO: Use common->createMmapBuffer after interface update.
+ }
+ return false;
+ }
+ bool supportsCreateMmapBuffer() override {
+ if (!mHasCreateMmapBuffer.has_value()) {
+ if (mInterfaceVersion > kAidlVersion3) {
+ mHasCreateMmapBuffer = true;
+ } else {
+ std::shared_ptr<IStreamCommon> common;
+ ndk::ScopedAStatus status = mStream->getStreamCommon(&common);
+ if (status.isOk()) {
+ VendorParameter createMmapBuffer{.id = kCreateMmapBuffer};
+ mHasCreateMmapBuffer =
+ common->setVendorParameters({createMmapBuffer}, false).isOk();
+ } else {
+ LOG(ERROR) << __func__ << ": getStreamCommon failed: " << status.getMessage();
+ return false;
+ }
+ }
+ }
+ return mHasCreateMmapBuffer.value();
+ }
private:
+ static constexpr const char* kCreateMmapBuffer = "aosp.createMmapBuffer";
+
WithAudioPortConfig mPortConfig;
std::shared_ptr<Stream> mStream;
StreamDescriptor mDescriptor;
std::optional<StreamContext> mContext;
std::shared_ptr<DefaultStreamCallback> mStreamCallback;
+ int32_t mInterfaceVersion = -1;
+ std::optional<bool> mHasCreateMmapBuffer;
};
SinkMetadata GenerateSinkMetadata(const AudioPortConfig& portConfig) {
@@ -3103,6 +3239,7 @@
const StreamContext* getStreamContext() const { return mStream->getContext(); }
StreamEventReceiver* getStreamEventReceiver() { return mStream->getEventReceiver(); }
std::shared_ptr<Stream> getStreamSharedPointer() const { return mStream->getSharedPointer(); }
+ StreamWorkerMethods* getStreamWorkerMethods() const { return mStream.get(); }
const std::string& skipTestReason() const { return mSkipTestReason; }
private:
@@ -3315,12 +3452,11 @@
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>(),
- {AudioOutputFlags::MMAP_NOIRQ, AudioOutputFlags::VOIP_RX,
- AudioOutputFlags::INCALL_MUSIC}) ||
+ {AudioOutputFlags::VOIP_RX, AudioOutputFlags::INCALL_MUSIC}) ||
(isBitPositionFlagSet(portConfig.flags.value().template get<AudioIoFlags::output>(),
AudioOutputFlags::COMPRESS_OFFLOAD) &&
!getMediaFileInfoForConfig(portConfig))));
@@ -3331,6 +3467,12 @@
return device.type.type == AudioDeviceType::IN_ECHO_REFERENCE;
}
+// MMap implementation on the HAL version <= 3 was not test compliant,
+// unless the stream provides 'createMmapBuffer'
+static bool skipStreamIoTestForStream(const StreamContext* context, StreamWorkerMethods* stream) {
+ return context->isMmapped() && !stream->supportsCreateMmapBuffer();
+}
+
template <typename Stream>
class StreamFixtureWithWorker {
public:
@@ -3376,7 +3518,8 @@
makeBurstCommands(mIsSync, burstCount, standbyInputWhenDone),
context->getFrameSizeBytes(), context->isMmapped());
mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
- *context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
+ *context, mWorkerDriver.get(), mStream->getStreamWorkerMethods(),
+ mStream->getStreamEventReceiver());
LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
ASSERT_TRUE(mWorker->start());
}
@@ -3419,6 +3562,10 @@
if (skipStreamIoTestForMixPortConfig(mStream->getPortConfig())) {
mSkipTestReason = "Mix port config is not supported for stream I/O tests";
}
+ if (skipStreamIoTestForStream(mStream->getStreamContext(),
+ mStream->getStreamWorkerMethods())) {
+ mSkipTestReason = "Stream can not be used in I/O tests";
+ }
}
const bool mIsSync;
@@ -3774,6 +3921,7 @@
ASSERT_EQ("", stream.skipTestReason());
StreamLogicDriverInvalidCommand driver(seq.second);
typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+ stream.getStreamWorkerMethods(),
stream.getStreamEventReceiver());
LOG(DEBUG) << __func__ << ": starting worker...";
ASSERT_TRUE(worker.start());
@@ -4373,11 +4521,15 @@
ASSERT_NO_FATAL_FAILURE(
stream.SetUpStreamForMixPortConfig(module.get(), moduleConfig.get(), portConfig));
if (skipStreamIoTestForDevice(stream.getDevice())) return;
+ if (skipStreamIoTestForStream(stream.getStreamContext(), stream.getStreamWorkerMethods())) {
+ return;
+ }
ASSERT_EQ("", stream.skipTestReason());
StreamLogicDefaultDriver driver(commandsAndStates,
stream.getStreamContext()->getFrameSizeBytes(),
stream.getStreamContext()->isMmapped());
typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+ stream.getStreamWorkerMethods(),
stream.getStreamEventReceiver());
LOG(DEBUG) << __func__ << ": starting worker...";
@@ -4407,10 +4559,14 @@
if (skipStreamIoTestForDevice(stream.getDevice())) return;
ASSERT_EQ("", stream.skipTestReason());
ASSERT_NO_FATAL_FAILURE(stream.TeardownPatchSetUpStream(module.get()));
+ if (skipStreamIoTestForStream(stream.getStreamContext(), stream.getStreamWorkerMethods())) {
+ return;
+ }
StreamLogicDefaultDriver driver(commandsAndStates,
stream.getStreamContext()->getFrameSizeBytes(),
stream.getStreamContext()->isMmapped());
typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+ stream.getStreamWorkerMethods(),
stream.getStreamEventReceiver());
ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get()));
@@ -4788,7 +4944,7 @@
// Allow optional routing via the TRANSFERRING state on bursts.
StateDag::Node makeAsyncBurstCommands(StateDag* d, size_t burstCount, StateDag::Node last) {
using State = StreamDescriptor::State;
- std::reference_wrapper<std::remove_reference_t<StateDag::Node>> prev = last;
+ std::reference_wrapper<StateDag::value_type> prev = last;
for (size_t i = 0; i < burstCount; ++i) {
StateDag::Node active = d->makeNode(State::ACTIVE, kBurstCommand, prev);
active.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, prev));
diff --git a/security/rkp/README.md b/security/rkp/README.md
index 43a00fb..ef52c0c 100644
--- a/security/rkp/README.md
+++ b/security/rkp/README.md
@@ -240,28 +240,35 @@
### Support for Android Virtualization Framework
-The Android Virtualization Framwork (AVF) relies on RKP to provision keys for VMs. A
-privileged vm, the RKP VM, is reponsible for generating and managing the keys for client
-VMs that run virtualized workloads. See the following for more background information on the
-RKP VM:
-* [rkp-vm](https://android.googlesource.com/platform/packages/modules/Virtualization/+/main/service_vm/README.md#rkp-vm-remote-key-provisioning-virtual-machine)
-* [rkp-service](https://source.android.com/docs/core/ota/modular-system/remote-key-provisioning#stack-architecture)
+The Android Virtualization Framework (AVF) relies on RKP to provision keys for
+VMs. There are a privileged set of VMs that RKP will recognise and provision
+keys to for specific applications, like Widevine, and for services, like
+[VM attestation][vm-attestation]. These privileged VMs are identified by their
+DICE chain through a combination of the [RKP VM marker][rkp-vm-marker]
+(key `-70006`) and the component name.
-It is important to distinquish the RKP VM from other components, such as KeyMint. An
-[RKP VM marker](https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#configuration-descriptor)
-(key `-70006`) is used for this purpose. The existence or absence of this marker is used to
-identify the type of component decribed by a given DICE chain.
+[vm-attestation]: http://android.googlesource.com/platform/packages/modules/Virtualization/+/main/docs/vm_remote_attestation.md
+[rkp-vm-marker]: https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#configuration-descriptor
-The following describes which certificate types may be request based on the RKP VM marker:
-1. "rkp-vm": If a DICE chain has zero or more certificates without the RKP VM
- marker followed by one or more certificates with the marker, then that chain
- describes an RKP VM. If there are further certificates without the RKP VM
- marker, then the chain does not describe an RKP VM.
+If a DICE chain begins from the root with zero or more certificates without
+the RKP VM marker, followed by only certificates with the marker up to and
+including the leaf certificate, then that chain describes a VM that RKP might
+provision keys to. Implementations must include the first RKP VM marker as early
+as possible after the point of divergence between TEE and non-TEE components in
+the DICE chain, prior to loading the Android Bootloader (ABL).
- Implementations must include the first RKP VM marker as early as possible
- after the point of divergence between TEE and non-TEE components in the DICE
- chain, prior to loading the Android Bootloader (ABL).
-2. "widevine" or "keymint": If there are no certificates with the RKP VM
- marker then it describes a TEE component.
-3. None: Any component described by a DICE chain that does not match the above
- two categories.
+The component name of the leaf certificate then identifies the kind of keys for
+RKP to provision:
+
+* "rkp-vm": for VM attestation keys managed by the [service VM][service-vm]
+* "keymint": for Android attestation keys
+* "widevine": for Widevine keys
+
+[service-vm]: https://android.googlesource.com/platform/packages/modules/Virtualization/+/main/service_vm/README.md#rkp-vm-remote-key-provisioning-virtual-machine
+
+If there are no certificates with the RKP VM marker in the DICE chain then it
+describes a TEE component that can be provisioned with Widevine and Android
+attestation keys.
+
+Any remaining DICE chains describe a component to which RKP will not provision
+keys.
\ No newline at end of file