From ce33864049635d3700fcd751360d1739026780cf Mon Sep 17 00:00:00 2001 From: Vlad Popa Date: Thu, 21 Sep 2023 18:54:03 -0700 Subject: [PATCH] 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 --- audio/aidl/default/Android.bp | 15 ++++ audio/aidl/default/Module.cpp | 7 +- audio/aidl/default/SoundDose.cpp | 89 +++++++++++++++++++ audio/aidl/default/Stream.cpp | 12 +++ audio/aidl/default/include/core-impl/Module.h | 2 +- .../default/include/core-impl/SoundDose.h | 57 ++++++++++-- audio/aidl/default/include/core-impl/Stream.h | 10 +++ .../default/include/core-impl/StreamPrimary.h | 4 + audio/aidl/default/primary/StreamPrimary.cpp | 15 +++- 9 files changed, 202 insertions(+), 9 deletions(-) diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index bb8d76f8fc..af12e75e8c 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -46,11 +46,20 @@ cc_library { "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 @@ cc_library { 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 @@ cc_binary { ], 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 76132b38fe..c17e0becfb 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -189,6 +189,11 @@ ndk::ScopedAStatus Module::createStreamContext( StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst, mVendorDebug.forceSynchronousDrain}; + std::shared_ptr 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(1, true /*configureEventFlagWord*/), std::make_unique(1, true /*configureEventFlagWord*/), @@ -196,7 +201,7 @@ ndk::ScopedAStatus Module::createStreamContext( portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags, portConfigIt->ext.get().handle, std::make_unique(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 f12ce5d806..1c9e081353 100644 --- a/audio/aidl/default/SoundDose.cpp +++ b/audio/aidl/default/SoundDose.cpp @@ -18,7 +18,15 @@ #include "core-impl/SoundDose.h" +#include #include +#include +#include + +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 @@ ndk::ScopedAStatus SoundDose::setOutputRs2UpperBound(float in_rs2ValueDbA) { 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 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( 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 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( 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& 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(mels.begin() + offset, mels.begin() + offset + length); + + mCallback->onNewMelValues(melRecord, mAudioDevice.value()); +} + +void SoundDose::MelCallback::onNewMelValues(const std::vector& 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 f7298c0286..f00e35833a 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -90,6 +90,14 @@ bool StreamContext::isValid() const { 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 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep 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 bfdab51454..b5c92a230b 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -157,7 +157,7 @@ class Module : public BnModule { bool mMicMute = false; bool mMasterMute = false; float mMasterVolume = 1.0f; - ChildInterface mSoundDose; + ChildInterface mSoundDose; std::optional mIsMmapSupported; protected: diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h index 2a069d9bac..82c1077f1b 100644 --- a/audio/aidl/default/include/core-impl/SoundDose.h +++ b/audio/aidl/default/include/core-impl/SoundDose.h @@ -20,23 +20,68 @@ #include #include - -using aidl::android::media::audio::common::AudioDevice; +#include +#include +#include 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::make(this)){}; + + // -------------------------------------- BnSoundDose ------------------------------------------ ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override; ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override; ndk::ScopedAStatus registerSoundDoseCallback( const std::shared_ptr& 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 mCallback; - float mRs2Value; + class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback { + public: + explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {} + + // ------------------------------------ MelCallback ---------------------------------------- + void onNewMelValues(const std::vector& 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& 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 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 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 88fddec233..daa920d9b5 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -44,6 +44,7 @@ #include #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 @@ class StreamContext { int32_t mixPortHandle, std::unique_ptr dataMQ, std::shared_ptr asyncCallback, std::shared_ptr outEventCallback, + std::weak_ptr streamDataProcessor, DebugParameters debugParameters) : mCommandMQ(std::move(commandMQ)), mInternalCommandCookie(std::rand()), @@ -100,6 +102,7 @@ class StreamContext { mDataMQ(std::move(dataMQ)), mAsyncCallback(asyncCallback), mOutEventCallback(outEventCallback), + mStreamDataProcessor(streamDataProcessor), mDebugParameters(debugParameters) {} StreamContext(StreamContext&& other) : mCommandMQ(std::move(other.mCommandMQ)), @@ -114,6 +117,7 @@ class StreamContext { 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 @@ class StreamContext { 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 @@ class StreamContext { std::shared_ptr getOutEventCallback() const { return mOutEventCallback; } + std::weak_ptr 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 @@ class StreamContext { std::unique_ptr mDataMQ; std::shared_ptr mAsyncCallback; std::shared_ptr mOutEventCallback; // Only used by output streams + std::weak_ptr 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 b3ddd0bd53..b64b749ec5 100644 --- a/audio/aidl/default/include/core-impl/StreamPrimary.h +++ b/audio/aidl/default/include/core-impl/StreamPrimary.h @@ -79,6 +79,10 @@ class StreamOutPrimary final : public StreamOut, ndk::ScopedAStatus getHwVolume(std::vector* _aidl_return) override; ndk::ScopedAStatus setHwVolume(const std::vector& 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 e01be8a3c6..17de2baf7a 100644 --- a/audio/aidl/default/primary/StreamPrimary.cpp +++ b/audio/aidl/default/primary/StreamPrimary.cpp @@ -37,7 +37,9 @@ using android::base::GetBoolProperty; 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 StreamPrimary::getDeviceProfiles() { static const std::vector kBuiltInSource{ @@ -183,4 +185,15 @@ ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector& in_ch 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