CSD: notify when RS2 momentary exposure is reached

RS2 is defined in IEC 62368-1 3rd edition as 100dBA. Added however the
possibility to further restrict this value as part of the momentary
exposure API.

Test: UT and manual tests
Bug: 256616352
Change-Id: I004a620e5e7915824d390f7ddc41c16f9675d45a
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index 88500b8..6fc756b 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -65,9 +65,12 @@
             std::lock_guard _lAf(mAudioFlinger.mLock);
             auto thread = mAudioFlinger.checkPlaybackThread_l(streamHandle);
             if (thread != nullptr) {
-                thread->startMelComputation(mSoundDoseManager.getOrCreateCallbackForDevice(
+                thread->startMelComputation(mSoundDoseManager.getOrCreateProcessorForDevice(
                     deviceId,
-                    newPatch.streamHandle));
+                    newPatch.streamHandle,
+                    thread->mSampleRate,
+                    thread->mChannelCount,
+                    thread->mFormat));
             }
         }
     }
@@ -97,11 +100,11 @@
 
     // Stop MEL calculation for the PlaybackThread
     std::lock_guard _lAf(mAudioFlinger.mLock);
+    mSoundDoseManager.removeStreamProcessor(melPatch.streamHandle);
     auto thread = mAudioFlinger.checkPlaybackThread_l(melPatch.streamHandle);
     if (thread != nullptr) {
         thread->stopMelComputation();
     }
-    mSoundDoseManager.removeStreamCallback(melPatch.streamHandle);
 }
 
 std::string AudioFlinger::MelReporter::dump() {
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index f35944b..acbbe48 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3405,14 +3405,11 @@
     return bytesWritten;
 }
 
-void AudioFlinger::PlaybackThread::startMelComputation(const sp<
-        audio_utils::MelProcessor::MelCallback>& callback)
+void AudioFlinger::PlaybackThread::startMelComputation(
+        const sp<audio_utils::MelProcessor>& processor)
 {
-    ALOGV("%s: creating new mel processor for thread %d", __func__, id());
-    mMelProcessor = sp<audio_utils::MelProcessor>::make(mSampleRate,
-                                                        mChannelCount,
-                                                        mFormat,
-                                                        callback);
+    ALOGV("%s: starting mel processor for thread %d", __func__, id());
+    mMelProcessor = processor;
 }
 
 void AudioFlinger::PlaybackThread::stopMelComputation() {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a7028ee..8bf629b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1091,8 +1091,7 @@
                     return INVALID_OPERATION;
                 }
 
-                void startMelComputation(const sp
-                             <audio_utils::MelProcessor::MelCallback>& callback);
+                void startMelComputation(const sp<audio_utils::MelProcessor>& processor);
                 void stopMelComputation();
 
 protected:
diff --git a/services/audioflinger/sounddose/Android.bp b/services/audioflinger/sounddose/Android.bp
index eb9517e..5c72fba 100644
--- a/services/audioflinger/sounddose/Android.bp
+++ b/services/audioflinger/sounddose/Android.bp
@@ -10,9 +10,7 @@
 cc_library {
     name: "libsounddose",
 
-    product_available: true,
     double_loadable: true,
-    host_supported: true,
 
     srcs: [
         "SoundDoseManager.cpp",
diff --git a/services/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 780d796..0a69c52 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -40,58 +40,95 @@
 
 }  // namespace
 
-sp<audio_utils::MelProcessor::MelCallback> SoundDoseManager::getOrCreateCallbackForDevice(
+sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice(
         audio_port_handle_t deviceId,
-        audio_io_handle_t streamHandle)
+        audio_io_handle_t streamHandle,
+        uint32_t sampleRate,
+        size_t channelCount,
+        audio_format_t format)
 {
     std::lock_guard _l(mLock);
 
-    auto streamHandleCallback = mActiveCallbacks.find(streamHandle);
-    if (streamHandleCallback != mActiveCallbacks.end()) {
+    auto streamProcessor = mActiveProcessors.find(streamHandle);
+    sp<audio_utils::MelProcessor> processor;
+    if (streamProcessor != mActiveProcessors.end()
+            && (processor = streamProcessor->second.promote())) {
         ALOGV("%s: found callback for stream %d", __func__, streamHandle);
-        auto callback = streamHandleCallback->second;
-        callback->mDeviceHandle = deviceId;
-        return callback;
+        processor->setDeviceId(deviceId);
+        return processor;
     } else {
         ALOGV("%s: creating new callback for device %d", __func__, streamHandle);
-        sp<Callback> melCallback = sp<Callback>::make(*this, deviceId);
-        mActiveCallbacks[streamHandle] = melCallback;
-        return melCallback;
+        sp<audio_utils::MelProcessor> melProcessor =
+            sp<audio_utils::MelProcessor>::make(sampleRate,
+                                                channelCount,
+                                                format,
+                                                *this,
+                                                deviceId,
+                                                mRs2Value);
+        mActiveProcessors[streamHandle] = melProcessor;
+        return melProcessor;
     }
 }
 
-void SoundDoseManager::removeStreamCallback(audio_io_handle_t streamHandle)
-{
-    std::unordered_map<audio_io_handle_t, sp<Callback>>::iterator callbackToRemove;
 
-    std::lock_guard _l(mLock);
-    callbackToRemove = mActiveCallbacks.find(streamHandle);
-    if (callbackToRemove != mActiveCallbacks.end()) {
-        mActiveCallbacks.erase(callbackToRemove);
-    }
-}
-
-void SoundDoseManager::Callback::onNewMelValues(const std::vector<float>& mels,
-                                                size_t offset,
-                                                size_t length) const
+void SoundDoseManager::setOutputRs2(float rs2Value)
 {
     ALOGV("%s", __func__);
-    std::lock_guard _l(mSoundDoseManager.mLock);
+    std::lock_guard _l(mLock);
+
+    for (auto& streamProcessor : mActiveProcessors) {
+        sp<audio_utils::MelProcessor> processor = streamProcessor.second.promote();
+        if (processor != nullptr) {
+            status_t result = processor->setOutputRs2(rs2Value);
+            if (result != NO_ERROR) {
+                ALOGW("%s: could not set RS2 value %f for stream %d",
+                      __func__,
+                      rs2Value,
+                      streamProcessor.first);
+            }
+        }
+    }
+}
+
+void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle)
+{
+    std::lock_guard _l(mLock);
+    auto callbackToRemove = mActiveProcessors.find(streamHandle);
+    if(callbackToRemove != mActiveProcessors.end()) {
+        mActiveProcessors.erase(callbackToRemove);
+    }
+}
+
+void SoundDoseManager::onNewMelValues(const std::vector<float>& mels,
+                                      size_t offset,
+                                      size_t length,
+                                      audio_port_handle_t deviceId) const
+{
+    ALOGV("%s", __func__);
+    std::lock_guard _l(mLock);
 
     int64_t timestampSec = getMonotonicSecond();
 
     // only for internal callbacks
-    mSoundDoseManager.mMelAggregator.aggregateAndAddNewMelRecord(
-        audio_utils::MelRecord(mDeviceHandle, std::vector<float>(
+    mMelAggregator->aggregateAndAddNewMelRecord(
+        audio_utils::MelRecord(deviceId, std::vector<float>(
                                    mels.begin() + offset,
                                    mels.begin() + offset + length),
                                timestampSec - length));
 }
 
+void SoundDoseManager::onMomentaryExposure(float currentMel,
+                                           audio_port_handle_t deviceId) const {
+    ALOGV("%s: Momentary exposure for device %d triggered: %f MEL",
+          __func__,
+          deviceId,
+          currentMel);
+}
+
 std::string SoundDoseManager::dump() const
 {
     std::string output;
-    mMelAggregator.foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
+    mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
         base::StringAppendF(&output,
                             "CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]",
                             csdRecord.value,
@@ -102,7 +139,7 @@
     });
 
     base::StringAppendF(&output, "\nCached Mel Records:\n");
-    mMelAggregator.foreachCachedMel([&output](const audio_utils::MelRecord& melRecord) {
+    mMelAggregator->foreachCachedMel([&output](const audio_utils::MelRecord& melRecord) {
         base::StringAppendF(&output, "Continuous MELs for portId=%d, ", melRecord.portId);
         base::StringAppendF(&output, "starting at timestamp %" PRId64 ": ", melRecord.timestamp);
 
@@ -116,7 +153,7 @@
 }
 
 size_t SoundDoseManager::getCachedMelRecordsSize() const {
-    return mMelAggregator.getCachedMelRecordsSize();
+    return mMelAggregator->getCachedMelRecordsSize();
 }
 
 }  // namespace android
diff --git a/services/audioflinger/sounddose/SoundDoseManager.h b/services/audioflinger/sounddose/SoundDoseManager.h
index fae5ba7..5934d2e 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.h
+++ b/services/audioflinger/sounddose/SoundDoseManager.h
@@ -21,60 +21,76 @@
 #include <audio_utils/MelAggregator.h>
 #include <mutex>
 #include <unordered_map>
+#include <utils/Errors.h>
 
 namespace android {
 
-/** CSD is computed with a rolling window of 7 days. */
-constexpr int64_t kCsdWindowSeconds = 604800;  // 60 * 60 * 24 * 7
-
-class SoundDoseManager {
+class SoundDoseManager : public audio_utils::MelProcessor::MelCallback {
 public:
-    SoundDoseManager() : mMelAggregator(kCsdWindowSeconds) {};
+    /** CSD is computed with a rolling window of 7 days. */
+    static constexpr int64_t kCsdWindowSeconds = 604800;  // 60s * 60m * 24h * 7d
+    /** Default RS2 value in dBA as defined in IEC 62368-1 3rd edition. */
+    static constexpr float kDefaultRs2Value = 100.f;
+
+    SoundDoseManager()
+        : mMelAggregator(sp<audio_utils::MelAggregator>::make(kCsdWindowSeconds)),
+          mRs2Value(kDefaultRs2Value) {};
 
     /**
-     * \brief Creates or gets the callback assigned to the streamHandle
+     * \brief Creates or gets the MelProcessor assigned to the streamHandle
      *
      * \param deviceId          id for the devices where the stream is active.
      * \param streanHandle      handle to the stream
+     * \param sampleRate        sample rate for the processor
+     * \param channelCount      number of channels to be processed.
+     * \param format            format of the input samples.
+     *
+     * \return MelProcessor assigned to the stream and device id.
      */
-    sp<audio_utils::MelProcessor::MelCallback> getOrCreateCallbackForDevice(
+    sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(
         audio_port_handle_t deviceId,
-        audio_io_handle_t streamHandle);
+        audio_io_handle_t streamHandle,
+        uint32_t sampleRate,
+        size_t channelCount,
+        audio_format_t format);
 
     /**
-     * \brief Removes stream callback when MEL computation is not needed anymore
+     * \brief Removes stream processor when MEL computation is not needed anymore
      *
      * \param streanHandle      handle to the stream
      */
-    void removeStreamCallback(audio_io_handle_t streamHandle);
+    void removeStreamProcessor(audio_io_handle_t streamHandle);
+
+    /**
+     * Sets the output RS2 value for momentary exposure warnings. Must not be
+     * higher than 100dBA and not lower than 80dBA.
+     *
+     * \param rs2Value value to use for momentary exposure
+     */
+    void setOutputRs2(float rs2Value);
 
     std::string dump() const;
 
     // used for testing
     size_t getCachedMelRecordsSize() const;
+
+    // ------ Override audio_utils::MelProcessor::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;
 private:
-    /**
-     * An implementation of the MelProcessor::MelCallback that is assigned to a
-     * specific device.
-     */
-    class Callback : public audio_utils::MelProcessor::MelCallback {
-    public:
-        Callback(SoundDoseManager& soundDoseManager, audio_port_handle_t deviceHandle)
-            : mSoundDoseManager(soundDoseManager), mDeviceHandle(deviceHandle) {}
-
-        void onNewMelValues(const std::vector<float>& mels,
-                            size_t offset,
-                            size_t length) const override;
-
-        SoundDoseManager& mSoundDoseManager;
-        audio_port_handle_t mDeviceHandle;
-    };
+    mutable std::mutex mLock;
 
     // no need for lock since MelAggregator is thread-safe
-    audio_utils::MelAggregator mMelAggregator;
+    const sp<audio_utils::MelAggregator> mMelAggregator;
 
-    std::mutex mLock;
-    std::unordered_map<audio_io_handle_t, sp<Callback>> mActiveCallbacks GUARDED_BY(mLock);
+    std::unordered_map<audio_io_handle_t,
+                       wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock);
+
+    float mRs2Value GUARDED_BY(mLock);
 };
 
 }  // namespace android
diff --git a/services/audioflinger/sounddose/tests/Android.bp b/services/audioflinger/sounddose/tests/Android.bp
index cb10950..d1779c2 100644
--- a/services/audioflinger/sounddose/tests/Android.bp
+++ b/services/audioflinger/sounddose/tests/Android.bp
@@ -9,7 +9,6 @@
 
 cc_test {
     name: "sounddosemanager_tests",
-    host_supported: true,
 
     srcs: [
         "sounddosemanager_tests.cpp"
diff --git a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
index 3fa7d6c..0aa5a20 100644
--- a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
+++ b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
@@ -24,35 +24,49 @@
 namespace android {
 namespace {
 
-TEST(SoundDoseManagerTest, GetCallbackForExistingStream) {
+TEST(SoundDoseManagerTest, GetProcessorForExistingStream) {
     SoundDoseManager soundDoseManager;
-    sp<audio_utils::MelProcessor::MelCallback> callback1 =
-        soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
-    sp<audio_utils::MelProcessor::MelCallback> callback2 =
-        soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/2, /*streamHandle=*/1);
+    sp<audio_utils::MelProcessor> processor1 =
+        soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+            /*streamHandle=*/1,
+            /*sampleRate*/44100,
+            /*channelCount*/2,
+            /*format*/AUDIO_FORMAT_PCM_FLOAT);
+    sp<audio_utils::MelProcessor> processor2 =
+        soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+            /*streamHandle=*/1,
+            /*sampleRate*/44100,
+            /*channelCount*/2,
+            /*format*/AUDIO_FORMAT_PCM_FLOAT);
 
-    EXPECT_EQ(callback1, callback2);
+    EXPECT_EQ(processor1, processor2);
 }
 
 TEST(SoundDoseManagerTest, RemoveExistingStream) {
     SoundDoseManager soundDoseManager;
-    sp<audio_utils::MelProcessor::MelCallback> callback1 =
-        soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
+    sp<audio_utils::MelProcessor> processor1 =
+        soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+            /*streamHandle=*/1,
+            /*sampleRate*/44100,
+            /*channelCount*/2,
+            /*format*/AUDIO_FORMAT_PCM_FLOAT);
 
-    soundDoseManager.removeStreamCallback(1);
-    sp<audio_utils::MelProcessor::MelCallback> callback2 =
-        soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/2, /*streamHandle=*/1);
+    soundDoseManager.removeStreamProcessor(1);
+    sp<audio_utils::MelProcessor> processor2 =
+        soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+            /*streamHandle=*/1,
+            /*sampleRate*/44100,
+            /*channelCount*/2,
+            /*format*/AUDIO_FORMAT_PCM_FLOAT);
 
-    EXPECT_NE(callback1, callback2);
+    EXPECT_NE(processor1, processor2);
 }
 
 TEST(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
     SoundDoseManager soundDoseManager;
     std::vector<float>mels{1, 1};
-    sp<audio_utils::MelProcessor::MelCallback> callback =
-        soundDoseManager.getOrCreateCallbackForDevice(/*deviceId=*/1, /*streamHandle=*/1);
 
-    callback->onNewMelValues(mels, 0, mels.size());
+    soundDoseManager.onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
 
     EXPECT_EQ(soundDoseManager.getCachedMelRecordsSize(), size_t{1});
 }