CSD: Add default AIDL HAL implementation
This should enable the sound dose gts on cuttlefish devices. The sound
dose HAL uses the internal MelProcessor to compute the MELs which are
reported to the framework.
Test: atest GtsAudioTestCases:SoundDoseTest
Bug: 301527435
Change-Id: Ifc505a0171bc8b4d3f5cf65d950fa5c0f812087f
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index bb8d76f..af12e75 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -46,11 +46,20 @@
"SoundDose.cpp",
],
shared_libs: [
+ "libaudio_aidl_conversion_common_ndk",
+ "libaudioutils",
"libbase",
"libbinder_ndk",
"libcutils",
"libutils",
],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ "-DBACKEND_NDK",
+ ],
visibility: [
"//hardware/interfaces/audio/aidl/sounddose/default",
],
@@ -111,8 +120,12 @@
shared_libs: [
"android.hardware.bluetooth.audio-V3-ndk",
"libaudio_aidl_conversion_common_ndk",
+ "libaudioutils",
+ "libaudioutils_nonvndk",
"libbluetooth_audio_session_aidl",
+ "liblog",
"libmedia_helper",
+ "libmediautils_vendor",
"libstagefright_foundation",
],
export_shared_lib_headers: [
@@ -143,8 +156,10 @@
],
shared_libs: [
"android.hardware.bluetooth.audio-V3-ndk",
+ "libaudioutils_nonvndk",
"libaudio_aidl_conversion_common_ndk",
"libbluetooth_audio_session_aidl",
+ "liblog",
"libmedia_helper",
"libstagefright_foundation",
],
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 76132b3..c17e0be 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -189,6 +189,11 @@
StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
mVendorDebug.forceTransientBurst,
mVendorDebug.forceSynchronousDrain};
+ std::shared_ptr<ISoundDose> soundDose;
+ if (!getSoundDose(&soundDose).isOk()) {
+ LOG(ERROR) << __func__ << ": could not create sound dose instance";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
StreamContext temp(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
@@ -196,7 +201,7 @@
portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
portConfigIt->ext.get<AudioPortExt::mix>().handle,
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
- asyncCallback, outEventCallback, params);
+ asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
if (temp.isValid()) {
*out_context = std::move(temp);
} else {
diff --git a/audio/aidl/default/SoundDose.cpp b/audio/aidl/default/SoundDose.cpp
index f12ce5d..1c9e081 100644
--- a/audio/aidl/default/SoundDose.cpp
+++ b/audio/aidl/default/SoundDose.cpp
@@ -18,7 +18,15 @@
#include "core-impl/SoundDose.h"
+#include <aidl/android/hardware/audio/core/sounddose/ISoundDose.h>
#include <android-base/logging.h>
+#include <media/AidlConversionCppNdk.h>
+#include <utils/Timers.h>
+
+using aidl::android::hardware::audio::core::sounddose::ISoundDose;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioFormatDescription;
namespace aidl::android::hardware::audio::core::sounddose {
@@ -28,11 +36,16 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ ::android::audio_utils::lock_guard l(mMutex);
mRs2Value = in_rs2ValueDbA;
+ if (mMelProcessor != nullptr) {
+ mMelProcessor->setOutputRs2UpperBound(in_rs2ValueDbA);
+ }
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus SoundDose::getOutputRs2UpperBound(float* _aidl_return) {
+ ::android::audio_utils::lock_guard l(mMutex);
*_aidl_return = mRs2Value;
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
return ndk::ScopedAStatus::ok();
@@ -44,6 +57,8 @@
LOG(ERROR) << __func__ << ": Callback is nullptr";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+
+ ::android::audio_utils::lock_guard l(mCbMutex);
if (mCallback != nullptr) {
LOG(ERROR) << __func__ << ": Sound dose callback was already registered";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@@ -51,7 +66,81 @@
mCallback = in_callback;
LOG(DEBUG) << __func__ << ": Registered sound dose callback ";
+
return ndk::ScopedAStatus::ok();
}
+void SoundDose::setAudioDevice(const AudioDevice& audioDevice) {
+ ::android::audio_utils::lock_guard l(mCbMutex);
+ mAudioDevice = audioDevice;
+}
+
+void SoundDose::startDataProcessor(uint32_t sampleRate, uint32_t channelCount,
+ const AudioFormatDescription& aidlFormat) {
+ ::android::audio_utils::lock_guard l(mMutex);
+ const auto result = aidl2legacy_AudioFormatDescription_audio_format_t(aidlFormat);
+ const audio_format_t format = result.value_or(AUDIO_FORMAT_INVALID);
+
+ if (mMelProcessor == nullptr) {
+ // we don't have the deviceId concept on the vendor side so just pass 0
+ mMelProcessor = ::android::sp<::android::audio_utils::MelProcessor>::make(
+ sampleRate, channelCount, format, mMelCallback, /*deviceId=*/0, mRs2Value);
+ } else {
+ mMelProcessor->updateAudioFormat(sampleRate, channelCount, format);
+ }
+}
+
+void SoundDose::process(const void* buffer, size_t bytes) {
+ ::android::audio_utils::lock_guard l(mMutex);
+ if (mMelProcessor != nullptr) {
+ mMelProcessor->process(buffer, bytes);
+ }
+}
+
+void SoundDose::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+ audio_port_handle_t deviceId __attribute__((__unused__))) const {
+ ::android::audio_utils::lock_guard l(mCbMutex);
+ if (!mAudioDevice.has_value()) {
+ LOG(WARNING) << __func__ << ": New mel values without a registered device";
+ return;
+ }
+ if (mCallback == nullptr) {
+ LOG(ERROR) << __func__ << ": New mel values without a registered callback";
+ return;
+ }
+
+ ISoundDose::IHalSoundDoseCallback::MelRecord melRecord;
+ melRecord.timestamp = nanoseconds_to_seconds(systemTime());
+ melRecord.melValues = std::vector<float>(mels.begin() + offset, mels.begin() + offset + length);
+
+ mCallback->onNewMelValues(melRecord, mAudioDevice.value());
+}
+
+void SoundDose::MelCallback::onNewMelValues(const std::vector<float>& mels, size_t offset,
+ size_t length,
+ audio_port_handle_t deviceId
+ __attribute__((__unused__))) const {
+ mSoundDose.onNewMelValues(mels, offset, length, deviceId);
+}
+
+void SoundDose::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
+ __attribute__((__unused__))) const {
+ ::android::audio_utils::lock_guard l(mCbMutex);
+ if (!mAudioDevice.has_value()) {
+ LOG(WARNING) << __func__ << ": Momentary exposure without a registered device";
+ return;
+ }
+ if (mCallback == nullptr) {
+ LOG(ERROR) << __func__ << ": Momentary exposure without a registered callback";
+ return;
+ }
+
+ mCallback->onMomentaryExposureWarning(currentMel, mAudioDevice.value());
+}
+
+void SoundDose::MelCallback::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
+ __attribute__((__unused__))) const {
+ mSoundDose.onMomentaryExposure(currentMel, deviceId);
+}
+
} // namespace aidl::android::hardware::audio::core::sounddose
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index f7298c0..f00e358 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -90,6 +90,14 @@
return true;
}
+void StreamContext::startStreamDataProcessor() {
+ auto streamDataProcessor = mStreamDataProcessor.lock();
+ if (streamDataProcessor != nullptr) {
+ streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout),
+ mFormat);
+ }
+}
+
void StreamContext::reset() {
mCommandMQ.reset();
mReplyMQ.reset();
@@ -593,6 +601,10 @@
fatal = true;
LOG(ERROR) << __func__ << ": write failed: " << status;
}
+ auto streamDataProcessor = mContext->getStreamDataProcessor().lock();
+ if (streamDataProcessor != nullptr) {
+ streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize);
+ }
} else {
if (mContext->getAsyncCallback() == nullptr) {
usleep(3000); // Simulate blocking transfer delay.
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index bfdab51..b5c92a2 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -157,7 +157,7 @@
bool mMicMute = false;
bool mMasterMute = false;
float mMasterVolume = 1.0f;
- ChildInterface<sounddose::ISoundDose> mSoundDose;
+ ChildInterface<sounddose::SoundDose> mSoundDose;
std::optional<bool> mIsMmapSupported;
protected:
diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h
index 2a069d9..82c1077 100644
--- a/audio/aidl/default/include/core-impl/SoundDose.h
+++ b/audio/aidl/default/include/core-impl/SoundDose.h
@@ -20,23 +20,68 @@
#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
-
-using aidl::android::media::audio::common::AudioDevice;
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <audio_utils/MelProcessor.h>
+#include <audio_utils/mutex.h>
namespace aidl::android::hardware::audio::core::sounddose {
-class SoundDose : public BnSoundDose {
+// Interface used for processing the data received by a stream.
+class StreamDataProcessorInterface {
public:
- SoundDose() : mRs2Value(DEFAULT_MAX_RS2){};
+ virtual ~StreamDataProcessorInterface() = default;
+ virtual void startDataProcessor(
+ uint32_t samplerate, uint32_t channelCount,
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0;
+ virtual void setAudioDevice(
+ const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0;
+ virtual void process(const void* buffer, size_t size) = 0;
+};
+
+class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface {
+ public:
+ SoundDose() : mMelCallback(::android::sp<MelCallback>::make(this)){};
+
+ // -------------------------------------- BnSoundDose ------------------------------------------
ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override;
ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override;
ndk::ScopedAStatus registerSoundDoseCallback(
const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) override;
+ // ----------------------------- StreamDataProcessorInterface ----------------------------------
+ void setAudioDevice(
+ const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override;
+ void startDataProcessor(
+ uint32_t samplerate, uint32_t channelCount,
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format) override;
+ void process(const void* buffer, size_t size) override;
+
private:
- std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback;
- float mRs2Value;
+ class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback {
+ public:
+ explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {}
+
+ // ------------------------------------ MelCallback ----------------------------------------
+ void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+ audio_port_handle_t deviceId) const override;
+ void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;
+
+ SoundDose& mSoundDose; // must outlive MelCallback, not owning
+ };
+
+ void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+ audio_port_handle_t deviceId) const;
+ void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const;
+
+ mutable ::android::audio_utils::mutex mCbMutex;
+ std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback GUARDED_BY(mCbMutex);
+ std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice
+ GUARDED_BY(mCbMutex);
+ mutable ::android::audio_utils::mutex mMutex;
+ float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2;
+ ::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex);
+ ::android::sp<MelCallback> mMelCallback GUARDED_BY(mMutex);
};
} // namespace aidl::android::hardware::audio::core::sounddose
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 88fddec..daa920d 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -44,6 +44,7 @@
#include <utils/Errors.h>
#include "core-impl/ChildInterface.h"
+#include "core-impl/SoundDose.h"
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
@@ -87,6 +88,7 @@
int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
std::shared_ptr<IStreamCallback> asyncCallback,
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
+ std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
DebugParameters debugParameters)
: mCommandMQ(std::move(commandMQ)),
mInternalCommandCookie(std::rand()),
@@ -100,6 +102,7 @@
mDataMQ(std::move(dataMQ)),
mAsyncCallback(asyncCallback),
mOutEventCallback(outEventCallback),
+ mStreamDataProcessor(streamDataProcessor),
mDebugParameters(debugParameters) {}
StreamContext(StreamContext&& other)
: mCommandMQ(std::move(other.mCommandMQ)),
@@ -114,6 +117,7 @@
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(std::move(other.mAsyncCallback)),
mOutEventCallback(std::move(other.mOutEventCallback)),
+ mStreamDataProcessor(std::move(other.mStreamDataProcessor)),
mDebugParameters(std::move(other.mDebugParameters)),
mFrameCount(other.mFrameCount) {}
StreamContext& operator=(StreamContext&& other) {
@@ -129,6 +133,7 @@
mDataMQ = std::move(other.mDataMQ);
mAsyncCallback = std::move(other.mAsyncCallback);
mOutEventCallback = std::move(other.mOutEventCallback);
+ mStreamDataProcessor = std::move(other.mStreamDataProcessor);
mDebugParameters = std::move(other.mDebugParameters);
mFrameCount = other.mFrameCount;
return *this;
@@ -154,6 +159,10 @@
std::shared_ptr<IStreamOutEventCallback> getOutEventCallback() const {
return mOutEventCallback;
}
+ std::weak_ptr<sounddose::StreamDataProcessorInterface> getStreamDataProcessor() const {
+ return mStreamDataProcessor;
+ }
+ void startStreamDataProcessor();
int getPortId() const { return mPortId; }
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
@@ -179,6 +188,7 @@
std::unique_ptr<DataMQ> mDataMQ;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
+ std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
DebugParameters mDebugParameters;
long mFrameCount = 0;
};
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index b3ddd0b..b64b749 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -79,6 +79,10 @@
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
+
+ ndk::ScopedAStatus setConnectedDevices(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
+ override;
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index e01be8a..17de2ba 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -37,7 +37,9 @@
namespace aidl::android::hardware::audio::core {
StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
- : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {}
+ : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {
+ context->startStreamDataProcessor();
+}
std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
static const std::vector<alsa::DeviceProfile> kBuiltInSource{
@@ -183,4 +185,15 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
+ if (!devices.empty()) {
+ auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
+ if (streamDataProcessor != nullptr) {
+ streamDataProcessor->setAudioDevice(devices[0]);
+ }
+ }
+ return StreamSwitcher::setConnectedDevices(devices);
+}
+
} // namespace aidl::android::hardware::audio::core