Merge "CSD: Add default AIDL HAL implementation" into main
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 d721b32..9fc99d4 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -213,6 +213,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*/),
@@ -220,7 +225,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 da94815..f407e25 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