Merge "ExtCam: Fix numAttempt is not increased in the while-loop" into main
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/Module.cpp b/audio/aidl/default/Module.cpp
index 123a5ec..aa624ff 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -214,24 +214,33 @@
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;
+ if (hasMmapFlag(flags)) {
+ MmapBufferDescriptor mmapDesc;
+ RETURN_STATUS_IF_ERROR(
+ createMmapBuffer(*portConfigIt, in_bufferSizeFrames, frameSize, &mmapDesc));
+ temp = StreamContext(
+ 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(mmapDesc),
+ outEventCallback, mSoundDose.getInstance(), params);
+ } else {
+ temp = StreamContext(
+ 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);
}
- 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 {
@@ -394,9 +403,10 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-ndk::ScopedAStatus Module::createMmapBuffer(
- const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
- ::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
+ndk::ScopedAStatus Module::createMmapBuffer(const AudioPortConfig& portConfig __unused,
+ int32_t bufferSizeFrames __unused,
+ int32_t frameSizeBytes __unused,
+ MmapBufferDescriptor* desc __unused) {
LOG(ERROR) << __func__ << ": " << mType << ": is not implemented";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
@@ -977,9 +987,6 @@
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));
@@ -1027,9 +1034,6 @@
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));
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/Stream.cpp b/audio/aidl/default/Stream.cpp
index 2800bed..873fc48 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -65,18 +65,26 @@
if (mReplyMQ) {
desc->reply = mReplyMQ->dupeDesc();
}
+ desc->frameSizeBytes = getFrameSize();
+ desc->bufferSizeFrames = getBufferSizeInFrames();
if (mDataMQ) {
- desc->frameSizeBytes = getFrameSize();
- desc->bufferSizeFrames = getBufferSizeInFrames();
desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
+ } else {
+ MmapBufferDescriptor mmapDesc; // Move-only due to `fd`.
+ mmapDesc.sharedMemory.fd = mMmapBufferDesc.sharedMemory.fd.dup();
+ mmapDesc.sharedMemory.size = mMmapBufferDesc.sharedMemory.size;
+ mmapDesc.burstSizeFrames = mMmapBufferDesc.burstSizeFrames;
+ mmapDesc.flags = mMmapBufferDesc.flags;
+ desc->audio.set<StreamDescriptor::AudioBuffer::Tag::mmap>(std::move(mmapDesc));
}
}
size_t StreamContext::getBufferSizeInFrames() const {
if (mDataMQ) {
return mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / getFrameSize();
+ } else {
+ return mMmapBufferDesc.sharedMemory.size / getFrameSize();
}
- return 0;
}
size_t StreamContext::getFrameSize() const {
@@ -96,9 +104,13 @@
LOG(ERROR) << "frame size is invalid";
return false;
}
- if (!hasMmapFlag(mFlags) && mDataMQ && !mDataMQ->isValid()) {
+ if (!isMmap() && mDataMQ && !mDataMQ->isValid()) {
LOG(ERROR) << "data FMQ is invalid";
return false;
+ } else if (isMmap() &&
+ (mMmapBufferDesc.sharedMemory.fd.get() == -1 ||
+ mMmapBufferDesc.sharedMemory.size == 0 || mMmapBufferDesc.burstSizeFrames == 0)) {
+ LOG(ERROR) << "mmap info is invalid" << mMmapBufferDesc.toString();
}
return true;
}
@@ -115,6 +127,7 @@
mCommandMQ.reset();
mReplyMQ.reset();
mDataMQ.reset();
+ mMmapBufferDesc.sharedMemory.fd.set(-1);
}
pid_t StreamWorkerCommonLogic::getTid() const {
@@ -128,7 +141,7 @@
std::string StreamWorkerCommonLogic::init() {
if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
- if (!hasMmapFlag(mContext->getFlags())) {
+ if (!mContext->isMmap()) {
StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
if (dataMQ == nullptr) return "Data MQ is null";
if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
@@ -167,7 +180,7 @@
} else {
reply->observable = reply->hardware = kUnknownPosition;
}
- if (hasMmapFlag(mContext->getFlags())) {
+ if (mContext->isMmap()) {
if (auto status = mDriver->getMmapPositionAndLatency(&reply->hardware, &reply->latencyMs);
status != ::android::OK) {
reply->hardware = kUnknownPosition;
@@ -252,9 +265,8 @@
mState == StreamDescriptor::State::ACTIVE ||
mState == StreamDescriptor::State::PAUSED ||
mState == StreamDescriptor::State::DRAINING) {
- if (bool success = hasMmapFlag(mContext->getFlags())
- ? readMmap(&reply)
- : read(fmqByteCount, &reply);
+ if (bool success =
+ mContext->isMmap() ? readMmap(&reply) : read(fmqByteCount, &reply);
!success) {
mState = StreamDescriptor::State::ERROR;
}
@@ -548,9 +560,8 @@
if (mState != StreamDescriptor::State::ERROR &&
mState != StreamDescriptor::State::TRANSFERRING &&
mState != StreamDescriptor::State::TRANSFER_PAUSED) {
- if (bool success = hasMmapFlag(mContext->getFlags())
- ? writeMmap(&reply)
- : write(fmqByteCount, &reply);
+ if (bool success = mContext->isMmap() ? writeMmap(&reply)
+ : write(fmqByteCount, &reply);
!success) {
mState = StreamDescriptor::State::ERROR;
}
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 0661015..379264d 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -212,8 +212,8 @@
const ::aidl::android::media::audio::common::AudioFormatDescription &format,
int32_t latencyMs, int32_t sampleRateHz, int32_t *bufferSizeFrames);
virtual ndk::ScopedAStatus createMmapBuffer(
- const ::aidl::android::hardware::audio::core::StreamContext& context,
- ::aidl::android::hardware::audio::core::StreamDescriptor* desc);
+ const ::aidl::android::media::audio::common::AudioPortConfig& portConfig,
+ int32_t bufferSizeFrames, int32_t frameSizeBytes, MmapBufferDescriptor* desc);
// Utility and helper functions accessible to subclasses.
static int32_t calculateBufferSizeFramesForPcm(int32_t latencyMs, int32_t sampleRateHz) {
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/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 376c684..bb790e9 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -104,6 +104,27 @@
mOutEventCallback(outEventCallback),
mStreamDataProcessor(streamDataProcessor),
mDebugParameters(debugParameters) {}
+ StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+ const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
+ int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
+ int32_t nominalLatencyMs, int32_t mixPortHandle, MmapBufferDescriptor&& mmapDesc,
+ std::shared_ptr<IStreamOutEventCallback> outEventCallback,
+ std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
+ DebugParameters debugParameters)
+ : mCommandMQ(std::move(commandMQ)),
+ mInternalCommandCookie(std::rand() | 1 /* make sure it's not 0 */),
+ mReplyMQ(std::move(replyMQ)),
+ mFormat(format),
+ mChannelLayout(channelLayout),
+ mSampleRate(sampleRate),
+ mFlags(flags),
+ mNominalLatencyMs(nominalLatencyMs),
+ mMixPortHandle(mixPortHandle),
+ mMmapBufferDesc(std::move(mmapDesc)),
+ mOutEventCallback(outEventCallback),
+ mStreamDataProcessor(streamDataProcessor),
+ mDebugParameters(debugParameters) {}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
@@ -136,6 +157,7 @@
bool isInput() const {
return mFlags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::input;
}
+ bool isMmap() const { return ::aidl::android::hardware::audio::common::hasMmapFlag(mFlags); }
bool isValid() const;
// 'reset' is called on a Binder thread when closing the stream. Does not use
// locking because it only cleans MQ pointers which were also set on the Binder thread.
@@ -155,7 +177,9 @@
::aidl::android::media::audio::common::AudioIoFlags mFlags;
int32_t mNominalLatencyMs;
int32_t mMixPortHandle;
+ // Only one of `mDataMQ` or `mMapBufferDesc` can be active, depending on `isMmap`
std::unique_ptr<DataMQ> mDataMQ;
+ MmapBufferDescriptor mMmapBufferDesc;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
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/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml b/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
index 4170b4c..c92e852 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
@@ -33,6 +33,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="{MODULE}" />
- <option name="native-test-timeout" value="30m" />
+ <option name="native-test-timeout" value="10m" />
</test>
</configuration>
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 2ce7b51..98f7d79 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -465,6 +465,7 @@
float fullScaleSineDb) {
ASSERT_NO_FATAL_FAILURE(SetUpDynamicsProcessingEffect());
SKIP_TEST_IF_DATA_UNSUPPORTED(mDescriptor.common.flags);
+ mInput.resize(kFrameCount * mChannelCount);
ASSERT_NO_FATAL_FAILURE(
generateSineWave(testFrequencies, mInput, 1.0, kSamplingFrequency, mChannelLayout));
mInputDb = calculateDb(mInput);
@@ -722,13 +723,10 @@
public DynamicsProcessingTestHelper {
public:
DynamicsProcessingInputGainDataTest()
- : DynamicsProcessingTestHelper((GetParam()), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
- }
+ : DynamicsProcessingTestHelper((GetParam()), AudioChannelLayout::LAYOUT_MONO) {}
void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(
- setUpDataTest({static_cast<int>(kInputFrequency)}, kSineFullScaleDb));
+ ASSERT_NO_FATAL_FAILURE(setUpDataTest({kInputFrequency}, kSineFullScaleDb));
}
void TearDown() override { TearDownDynamicsProcessingEffect(); }
@@ -851,15 +849,12 @@
: public ::testing::TestWithParam<LimiterConfigDataTestParams>,
public DynamicsProcessingTestHelper {
public:
- DynamicsProcessingLimiterConfigDataTest()
- : DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mBufferSize = kFrameCount * mChannelCount;
- mInput.resize(mBufferSize);
- }
+ DynamicsProcessingLimiterConfigDataTest(LimiterConfigDataTestParams param = GetParam(),
+ int32_t layout = AudioChannelLayout::LAYOUT_MONO)
+ : DynamicsProcessingTestHelper(param, layout) {}
void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(
- setUpDataTest({static_cast<int>(kInputFrequency)}, kSineFullScaleDb));
+ ASSERT_NO_FATAL_FAILURE(setUpDataTest({kInputFrequency}, kSineFullScaleDb));
}
void TearDown() override { TearDownDynamicsProcessingEffect(); }
@@ -876,12 +871,35 @@
ratio = inputOverThreshold / outputOverThreshold;
}
- void setLimiterParamsAndProcess(std::vector<float>& input, std::vector<float>& output) {
+ void setLimiterParamsAndProcess(std::vector<float>& input, std::vector<float>& output,
+ bool isEngineLimiterEnabled = true) {
+ mEngineConfigPreset.limiterInUse = isEngineLimiterEnabled;
addEngineConfig(mEngineConfigPreset);
addLimiterConfig(mLimiterConfigList);
EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(input, output));
}
+ void testEnableDisableConfiguration(bool isLimiterEnabled, bool isEngineLimiterEnabled) {
+ cleanUpLimiterConfig();
+ std::vector<float> output(mInput.size());
+ for (int i = 0; i < mChannelCount; i++) {
+ // Set non-default values
+ fillLimiterConfig(mLimiterConfigList, i, isLimiterEnabled, kDefaultLinkerGroup,
+ 5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
+ -20 /*threshold*/, 5 /*postgain*/);
+ }
+ ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output, isEngineLimiterEnabled));
+ float outputdB = calculateDb(output, kStartIndex);
+ if (isAllParamsValid()) {
+ if (isLimiterEnabled && isEngineLimiterEnabled) {
+ EXPECT_GT(std::abs(mInputDb - outputdB), kMinDifferenceDb)
+ << "Input level: " << mInputDb << " Output level: " << outputdB;
+ } else {
+ EXPECT_NEAR(mInputDb, outputdB, kLimiterTestToleranceDb);
+ }
+ }
+ }
+
void cleanUpLimiterConfig() {
CleanUp();
mLimiterConfigList.clear();
@@ -892,8 +910,9 @@
static constexpr float kDefaultRatio = 4;
static constexpr float kDefaultThreshold = -10;
static constexpr float kDefaultPostGain = 0;
- static constexpr float kInputFrequency = 1000;
static constexpr float kLimiterTestToleranceDb = 0.05;
+ static constexpr float kMinDifferenceDb = 5;
+ const std::vector<bool> kEnableValues = {true, false, true};
std::vector<DynamicsProcessing::LimiterConfig> mLimiterConfigList;
int mBufferSize;
};
@@ -975,25 +994,16 @@
}
TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisable) {
- std::vector<bool> limiterEnableValues = {false, true};
- std::vector<float> output(mInput.size());
- for (bool isEnabled : limiterEnableValues) {
- cleanUpLimiterConfig();
- for (int i = 0; i < mChannelCount; i++) {
- // Set non-default values
- fillLimiterConfig(mLimiterConfigList, i, isEnabled, kDefaultLinkerGroup,
- 5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
- -10 /*threshold*/, 5 /*postgain*/);
- }
- ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
- if (!isAllParamsValid()) {
- continue;
- }
- if (isEnabled) {
- EXPECT_NE(mInputDb, calculateDb(output, kStartIndex));
- } else {
- EXPECT_NEAR(mInputDb, calculateDb(output, kStartIndex), kLimiterTestToleranceDb);
- }
+ for (bool isLimiterEnabled : kEnableValues) {
+ ASSERT_NO_FATAL_FAILURE(
+ testEnableDisableConfiguration(isLimiterEnabled, true /*Engine Enabled*/));
+ }
+}
+
+TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisableViaEngine) {
+ for (bool isEngineLimiterEnabled : kEnableValues) {
+ ASSERT_NO_FATAL_FAILURE(
+ testEnableDisableConfiguration(true /*Limiter Enabled*/, isEngineLimiterEnabled));
}
}
@@ -1010,6 +1020,103 @@
});
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingLimiterConfigDataTest);
+class DynamicsProcessingLimiterLinkerDataTest : public DynamicsProcessingLimiterConfigDataTest {
+ public:
+ DynamicsProcessingLimiterLinkerDataTest()
+ : DynamicsProcessingLimiterConfigDataTest(GetParam(), AudioChannelLayout::LAYOUT_STEREO) {}
+
+ void calculateExpectedOutputDb(std::vector<float>& expectedOutputDb) {
+ std::vector<float> inputDbValues = calculateStereoDb(mInput, kStartIndex);
+ ASSERT_EQ(inputDbValues.size(), kRatioThresholdPairValues.size());
+ EXPECT_NEAR(inputDbValues[0], inputDbValues[1], kToleranceDb);
+ for (size_t i = 0; i < kRatioThresholdPairValues.size(); i++) {
+ const auto& [ratio, threshold] = kRatioThresholdPairValues[i];
+ expectedOutputDb.push_back((inputDbValues[i] - threshold) / ratio + threshold);
+ }
+ }
+
+ std::vector<float> calculateStereoDb(const std::vector<float>& input,
+ size_t startSamplePos = 0) {
+ std::vector<float> leftChannel;
+ std::vector<float> rightChannel;
+ for (size_t i = 0; i < input.size(); i += 2) {
+ leftChannel.push_back(input[i]);
+ if (i + 1 < input.size()) {
+ rightChannel.push_back(input[i + 1]);
+ }
+ }
+ return {calculateDb(leftChannel, startSamplePos),
+ calculateDb(rightChannel, startSamplePos)};
+ }
+
+ void setLinkGroupAndProcess(std::vector<float>& output, bool hasSameLinkGroup) {
+ for (int i = 0; i < mChannelCount; i++) {
+ const auto& [ratio, threshold] = kRatioThresholdPairValues[i];
+ ASSERT_NE(ratio, 0);
+ int linkGroup = hasSameLinkGroup ? kDefaultLinkerGroup : i;
+ fillLimiterConfig(mLimiterConfigList, i, true, linkGroup, kDefaultAttackTime,
+ kDefaultReleaseTime, ratio, threshold, kDefaultPostGain);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+
+ if (!isAllParamsValid()) {
+ GTEST_SKIP() << "Invalid parameters. Skipping the test\n";
+ }
+ }
+
+ const std::vector<std::pair<float, float>> kRatioThresholdPairValues = {{2, -10}, {5, -20}};
+};
+
+TEST_P(DynamicsProcessingLimiterLinkerDataTest, SameLinkGroupDifferentConfigs) {
+ std::vector<float> output(mInput.size());
+
+ ASSERT_NO_FATAL_FAILURE(setLinkGroupAndProcess(output, true));
+
+ std::vector<float> outputDbValues = calculateStereoDb(output, kStartIndex);
+
+ std::vector<float> expectedOutputDbValues;
+ ASSERT_NO_FATAL_FAILURE(calculateExpectedOutputDb(expectedOutputDbValues));
+
+ // Verify that the actual output dB is same as the calculated maximum attenuation.
+ float expectedOutputDb = std::min(expectedOutputDbValues[0], expectedOutputDbValues[1]);
+ EXPECT_NEAR(outputDbValues[0], expectedOutputDb, kToleranceDb);
+ EXPECT_NEAR(outputDbValues[1], expectedOutputDb, kToleranceDb);
+}
+
+TEST_P(DynamicsProcessingLimiterLinkerDataTest, DifferentLinkGroupDifferentConfigs) {
+ std::vector<float> output(mInput.size());
+
+ ASSERT_NO_FATAL_FAILURE(setLinkGroupAndProcess(output, false));
+
+ std::vector<float> outputDbValues = calculateStereoDb(output, kStartIndex);
+
+ std::vector<float> expectedOutputDbValues;
+ ASSERT_NO_FATAL_FAILURE(calculateExpectedOutputDb(expectedOutputDbValues));
+
+ // Verify that both channels have different compression levels
+ EXPECT_GT(abs(expectedOutputDbValues[0] - expectedOutputDbValues[1]), kMinDifferenceDb)
+ << "Left channel level: " << expectedOutputDbValues[0]
+ << " Right channel level: " << expectedOutputDbValues[1];
+
+ // Verify that the actual output and the calculated dB values are same
+ EXPECT_NEAR(outputDbValues[0], expectedOutputDbValues[0], kToleranceDb);
+ EXPECT_NEAR(outputDbValues[1], expectedOutputDbValues[1], kToleranceDb);
+}
+
+INSTANTIATE_TEST_SUITE_P(DynamicsProcessingTest, DynamicsProcessingLimiterLinkerDataTest,
+ testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+ [](const auto& info) {
+ auto descriptor = info.param;
+ std::string name = getPrefix(descriptor.second);
+ std::replace_if(
+ name.begin(), name.end(),
+ [](const char c) { return !std::isalnum(c); }, '_');
+ return name;
+ });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingLimiterLinkerDataTest);
+
/**
* Test DynamicsProcessing ChannelConfig
*/
@@ -1215,7 +1322,6 @@
public:
DynamicsProcessingEqBandConfigDataTest()
: DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
mBinOffsets.resize(mMultitoneTestFrequencies.size());
}
@@ -1444,7 +1550,6 @@
public:
DynamicsProcessingMbcBandConfigDataTest()
: DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
mBinOffsets.resize(mMultitoneTestFrequencies.size());
}
diff --git a/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl b/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl
index 451eaa9..5825395 100644
--- a/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl
+++ b/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl
@@ -88,8 +88,12 @@
ENCR_AES_CBC = 100,
AUTH_HMAC_SHA2_256_128 = 101,
- /** Unknown */
+ /** Misc placeholders */
+ // Unknown is now reserved for cases involving DRBs where an integrity algorithm
+ // is not assigned (which is different from a null algorithm being used) but a
+ // notification based solely on the integrity algorithm should not be triggered.
UNKNOWN = 113,
+ // Currently unused
OTHER = 114,
/** For proprietary algorithms */
diff --git a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
index b5aee5c..41200f9 100644
--- a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
+++ b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
@@ -41,7 +41,7 @@
if (mHal1_6) {
mHal1_6->getAllowedNetworkTypesBitmap(serial);
} else {
- mHal1_5->getPreferredNetworkType(serial);
+ mHal1_5->getPreferredNetworkTypeBitmap(serial);
}
return ok();
}
@@ -166,7 +166,7 @@
if (mHal1_6) {
mHal1_6->setAllowedNetworkTypesBitmap(serial, raf);
} else {
- mHal1_5->setPreferredNetworkType(serial, getNetworkTypeFromRaf(raf));
+ mHal1_5->setPreferredNetworkTypeBitmap(serial, raf);
}
return ok();
}
diff --git a/radio/aidl/compat/libradiocompat/network/RadioResponse-network.cpp b/radio/aidl/compat/libradiocompat/network/RadioResponse-network.cpp
index 5a98eb2..428070c 100644
--- a/radio/aidl/compat/libradiocompat/network/RadioResponse-network.cpp
+++ b/radio/aidl/compat/libradiocompat/network/RadioResponse-network.cpp
@@ -55,9 +55,10 @@
}
Return<void> RadioResponse::getPreferredNetworkTypeBitmapResponse(
- const V1_0::RadioResponseInfo& info, hidl_bitfield<V1_4::RadioAccessFamily>) {
+ const V1_0::RadioResponseInfo& info,
+ hidl_bitfield<V1_4::RadioAccessFamily> networkTypeBitmap) {
LOG_CALL << info.serial;
- LOG(ERROR) << "IRadio HAL 1.4 not supported";
+ networkCb()->getAllowedNetworkTypesBitmapResponse(toAidl(info), networkTypeBitmap);
return {};
}
@@ -290,7 +291,7 @@
Return<void> RadioResponse::setPreferredNetworkTypeBitmapResponse(
const V1_0::RadioResponseInfo& info) {
LOG_CALL << info.serial;
- LOG(ERROR) << "IRadio HAL 1.4 not supported";
+ networkCb()->setAllowedNetworkTypesBitmapResponse(toAidl(info));
return {};
}
diff --git a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
index 1623960..5df39ed 100644
--- a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
+++ b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
@@ -209,4 +209,15 @@
INSTANTIATE_TEST_SUITE_P(
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
- android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+ android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
index d7e4546..106ee29 100644
--- a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
+++ b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
@@ -94,3 +94,14 @@
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
index 26b2ded..98c8a9c 100644
--- a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
+++ b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
@@ -108,3 +108,14 @@
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
index 9678da4..da69b37 100644
--- a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -320,5 +320,10 @@
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
- return RUN_ALL_TESTS();
+ std::system("/system/bin/svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ std::system("/system/bin/svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
}
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 5236e90..195e47b 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -52,7 +52,7 @@
},
],
-
+ min_sdk_version: "35",
}
// An aidl_interface_defaults that includes the latest KeyMint AIDL interface.
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
diff --git a/security/rkp/aidl/Android.bp b/security/rkp/aidl/Android.bp
index adc63f6..df8a0ef 100644
--- a/security/rkp/aidl/Android.bp
+++ b/security/rkp/aidl/Android.bp
@@ -35,6 +35,7 @@
"//apex_available:platform",
"com.android.virt",
],
+ min_sdk_version: "35",
},
},
versions_with_info: [
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index d7e7b43..1d4ec58 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/aidl/Android.bp
@@ -28,4 +28,5 @@
},
},
versions: ["1"],
+ min_sdk_version: "35",
}
diff --git a/security/see/authmgr/aidl/README.md b/security/see/authmgr/aidl/README.md
index 97b2b1d..d7bb5e4 100644
--- a/security/see/authmgr/aidl/README.md
+++ b/security/see/authmgr/aidl/README.md
@@ -16,6 +16,9 @@
requirements that are specific to Android release versions.
### Android 16
-If implementing `IAuthMgrAuthorization` in Android 16 only one AuthMgr Backend is
+- If implementing `IAuthMgrAuthorization` in Android 16 only one AuthMgr Backend is
supported and dynamic service discovery is not supported. The AuthMgr Backend
-service must be exposed on secure partition ID 0x8001 over VSOCK port 1.
\ No newline at end of file
+service must be exposed on secure partition ID 0x8001 over VSOCK port 1.
+
+- AuthMgr Front Ends must implement the "android.16" profile as described in the
+[Android Profile for DICE](https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#versions)
\ No newline at end of file
diff --git a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
index b1590f9..77e2916 100644
--- a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
+++ b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
@@ -616,8 +616,18 @@
}
TEST_P(SensorsAidlTest, InjectSensorEventData) {
- std::vector<SensorInfo> sensors = getInjectEventSensors();
- if (sensors.size() == 0) {
+ std::vector<SensorInfo> sensorsAll = getInjectEventSensors();
+ std::vector<SensorInfo> sensors3d;
+
+ for (const auto& s : sensorsAll) {
+ if ((s.type == SensorType::ACCELEROMETER) || (s.type == SensorType::GYROSCOPE) ||
+ (s.type == SensorType::MAGNETIC_FIELD)) {
+ sensors3d.push_back(s);
+ }
+ }
+
+ // Here we only test for the sensors with vec3 data type
+ if (sensors3d.size() == 0) {
return;
}
@@ -645,7 +655,7 @@
Event::EventPayload::Vec3 data = {1, 2, 3, SensorStatus::ACCURACY_HIGH};
injectedEvent.payload.set<Event::EventPayload::Tag::vec3>(data);
- for (const auto& s : sensors) {
+ for (const auto& s : sensors3d) {
additionalInfoEvent.sensorHandle = s.sensorHandle;
ASSERT_TRUE(getSensors()->injectSensorData(additionalInfoEvent).isOk());
@@ -655,10 +665,10 @@
}
// Wait for events to be written back to the Event FMQ
- callback.waitForEvents(sensors, std::chrono::milliseconds(1000) /* timeout */);
+ callback.waitForEvents(sensors3d, std::chrono::milliseconds(1000) /* timeout */);
getEnvironment()->unregisterCallback();
- for (const auto& s : sensors) {
+ for (const auto& s : sensors3d) {
auto events = callback.getEvents(s.sensorHandle);
if (events.empty()) {
FAIL() << "Received no events";
diff --git a/uwb/aidl/vts/VtsHalUwbTargetTest.cpp b/uwb/aidl/vts/VtsHalUwbTargetTest.cpp
index 2b09f7e..c75160c 100644
--- a/uwb/aidl/vts/VtsHalUwbTargetTest.cpp
+++ b/uwb/aidl/vts/VtsHalUwbTargetTest.cpp
@@ -202,8 +202,26 @@
}
TEST_P(UwbAidl, ChipSendUciMessage_GetDeviceInfo) {
- const auto iuwb_chip = getAnyChipAndOpen();
+ std::promise<void> open_cb_promise;
+ std::future<void> open_cb_future{open_cb_promise.get_future()};
+ std::promise<void> init_cb_promise;
+ std::future<void> init_cb_future{init_cb_promise.get_future()};
+ std::shared_ptr<UwbClientCallback> callback = ndk::SharedRefBase::make<UwbClientCallback>(
+ [](auto /* data */) {},
+ [&init_cb_promise, &open_cb_promise](auto event, auto /* status */) {
+ if (event == UwbEvent::OPEN_CPLT) {
+ open_cb_promise.set_value();
+ }
+ if (event == UwbEvent::POST_INIT_CPLT) {
+ init_cb_promise.set_value();
+ }
+ });
+ std::chrono::milliseconds timeout{1000};
+ const auto iuwb_chip = getAnyChip();
+ EXPECT_TRUE(iuwb_chip->open(callback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
EXPECT_TRUE(iuwb_chip->coreInit().isOk());
+ EXPECT_EQ(init_cb_future.wait_for(timeout), std::future_status::ready);
std::vector<uint8_t> uciMessage = {0x20, 0x02, 0x00, 0x00}; /** CoreGetDeviceInfo */
int32_t* return_status = new int32_t;
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index f659bf6..1e6d555 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -21,6 +21,7 @@
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <aidl/android/hardware/wifi/BnWifiStaIfaceEventCallback.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <binder/IServiceManager.h>
@@ -29,6 +30,7 @@
#include "wifi_aidl_test_utils.h"
+using aidl::android::hardware::wifi::BnWifiStaIfaceEventCallback;
using aidl::android::hardware::wifi::CachedScanData;
using aidl::android::hardware::wifi::IWifi;
using aidl::android::hardware::wifi::IWifiStaIface;
@@ -379,6 +381,66 @@
}
}
+class WifiStaIfaceEventCallback : public BnWifiStaIfaceEventCallback {
+ public:
+ WifiStaIfaceEventCallback() = default;
+
+ ::ndk::ScopedAStatus onBackgroundFullScanResult(
+ int32_t /* in_cmdId */, int32_t /* in_bucketsScanned */,
+ const ::aidl::android::hardware::wifi::StaScanResult& /* in_result */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onBackgroundScanFailure(int32_t /* in_cmdId */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onBackgroundScanResults(
+ int32_t /* in_cmdId */,
+ const std::vector<::aidl::android::hardware::wifi::StaScanData>& /* in_scanDatas */)
+ override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onRssiThresholdBreached(int32_t /* in_cmdId */,
+ const std::array<uint8_t, 6>& /* in_currBssid */,
+ int32_t /* in_currRssi */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtFailure(int32_t /*in_cmdId*/,
+ ::aidl::android::hardware::wifi::IWifiStaIfaceEventCallback::
+ TwtErrorCode /* in_error */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionCreate(
+ int32_t /* in_cmdId */,
+ const ::aidl::android::hardware::wifi::TwtSession& /* in_twtSession */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionUpdate(
+ int32_t /* in_cmdId */,
+ const ::aidl::android::hardware::wifi::TwtSession& /* in_twtSession */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionTeardown(
+ int32_t /* in_cmdId */, int32_t /* in_twtSessionId */,
+ ::aidl::android::hardware::wifi::IWifiStaIfaceEventCallback::
+ TwtTeardownReasonCode /* in_reasonCode */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionStats(
+ int32_t /* in_cmdId */, int32_t /* in_twtSessionId */,
+ const ::aidl::android::hardware::wifi::TwtSessionStats& /* in_twtSessionStats */)
+ override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionSuspend(int32_t /* in_cmdId */,
+ int32_t /* in_twtSessionId */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onTwtSessionResume(int32_t /* in_cmdId */,
+ int32_t /* in_twtSessionId */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
TEST_P(WifiStaIfaceAidlTest, TwtGetCapabilities) {
if (interface_version_ < 2) {
GTEST_SKIP() << "TwtGetCapabilities is available as of sta_iface V2";
@@ -414,6 +476,10 @@
if (!twt_capabilities.isTwtRequesterSupported) {
GTEST_SKIP() << "TWT is not supported";
}
+ const std::shared_ptr<WifiStaIfaceEventCallback> callback =
+ ndk::SharedRefBase::make<WifiStaIfaceEventCallback>();
+ ASSERT_NE(callback, nullptr);
+ EXPECT_TRUE(wifi_sta_iface_->registerEventCallback(callback).isOk());
TwtRequest twtRequest;
twtRequest.mloLinkId = 0;