CSD: report the new csd values to AudioService

We are reporting with every new callback from the MelProcessor the
values that lead to a change of at least 1% of CSD.

Test: manual, logs
Bug: 257238734
Change-Id: I3f0c39c9fda62bfccd3401e065586ca8621627ca
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 57a4133..df3dec7 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -453,6 +453,7 @@
         "aidl/android/media/IAudioTrackCallback.aidl",
 
         "aidl/android/media/ISoundDoseCallback.aidl",
+        "aidl/android/media/SoundDoseRecord.aidl",
     ],
     imports: [
         "android.media.audio.common.types-V2",
@@ -551,6 +552,7 @@
     local_include_dir: "aidl",
     srcs: [
         "aidl/android/media/ISoundDoseCallback.aidl",
+        "aidl/android/media/SoundDoseRecord.aidl",
     ],
 
     double_loadable: true,
diff --git a/media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl b/media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl
index 3edf681..7e59409 100644
--- a/media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl
+++ b/media/libaudioclient/aidl/android/media/ISoundDoseCallback.aidl
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import android.media.SoundDoseRecord;
+
 /**
  * Interface used to push the sound dose related information from the audio
  * server to the AudioService#SoundDoseHelper.
@@ -23,4 +25,11 @@
 interface ISoundDoseCallback {
     /** Called whenever the momentary exposure exceeds the RS2 value. */
     oneway void onMomentaryExposure(float currentMel, int deviceId);
+
+    /**
+     * Notifies that the CSD value has changed. The currentCsd is normalized
+     * with value 1 representing 100% of sound dose. SoundDoseRecord represents
+     * the newest record that lead to the new currentCsd.
+     */
+    oneway void onNewCsdValue(float currentCsd, in SoundDoseRecord[] records);
 }
diff --git a/media/libaudioclient/aidl/android/media/SoundDoseRecord.aidl b/media/libaudioclient/aidl/android/media/SoundDoseRecord.aidl
new file mode 100644
index 0000000..94b8ce2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/SoundDoseRecord.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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.
+ */
+
+package android.media;
+
+/** Record containing information about the computed sound dose. */
+parcelable SoundDoseRecord {
+    /**
+     * Corresponds to the time in seconds when the CSD value is calculated from.
+     * Values should be consistent and referenced from the same clock (e.g.: monotonic)
+     */
+    long timestamp;
+    /** Corresponds to the duration that leads to the CSD value. */
+    int duration;
+    /** The actual contribution to the CSD computation normalized: 1.f is 100%CSD. */
+    float value;
+    /** The average MEL value in this time frame that lead to this CSD value. */
+    float averageMel;
+}
diff --git a/services/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 86fb3a4..474d0ce 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -21,9 +21,10 @@
 #include "SoundDoseManager.h"
 
 #include <android-base/stringprintf.h>
-#include <cinttypes>
-#include <utils/Log.h>
 #include <time.h>
+#include <utils/Log.h>
+#include <cinttypes>
+#include "android/media/SoundDoseRecord.h"
 
 namespace android {
 
@@ -41,38 +42,27 @@
 }  // namespace
 
 sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice(
-        audio_port_handle_t deviceId,
-        audio_io_handle_t streamHandle,
-        uint32_t sampleRate,
-        size_t channelCount,
-        audio_format_t format)
-{
+        audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate,
+        size_t channelCount, audio_format_t format) {
     std::lock_guard _l(mLock);
 
     auto streamProcessor = mActiveProcessors.find(streamHandle);
     sp<audio_utils::MelProcessor> processor;
-    if (streamProcessor != mActiveProcessors.end()
-            && (processor = streamProcessor->second.promote())) {
+    if (streamProcessor != mActiveProcessors.end() &&
+            (processor = streamProcessor->second.promote())) {
         ALOGV("%s: found callback for stream %d", __func__, streamHandle);
         processor->setDeviceId(deviceId);
         return processor;
     } else {
         ALOGV("%s: creating new callback for device %d", __func__, streamHandle);
-        sp<audio_utils::MelProcessor> melProcessor =
-            sp<audio_utils::MelProcessor>::make(sampleRate,
-                                                channelCount,
-                                                format,
-                                                *this,
-                                                deviceId,
-                                                mRs2Value);
+        sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make(
+                sampleRate, channelCount, format, *this, deviceId, mRs2Value);
         mActiveProcessors[streamHandle] = melProcessor;
         return melProcessor;
     }
 }
 
-
-void SoundDoseManager::setOutputRs2(float rs2Value)
-{
+void SoundDoseManager::setOutputRs2(float rs2Value) {
     ALOGV("%s", __func__);
     std::lock_guard _l(mLock);
 
@@ -81,48 +71,60 @@
         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,
+                ALOGW("%s: could not set RS2 value %f for stream %d", __func__, rs2Value,
                       streamProcessor.first);
             }
         }
     }
 }
 
-void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle)
-{
+void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) {
     std::lock_guard _l(mLock);
     auto callbackToRemove = mActiveProcessors.find(streamHandle);
-    if(callbackToRemove != mActiveProcessors.end()) {
+    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
-{
+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();
+    sp<media::ISoundDoseCallback> soundDoseCallback;
+    std::vector<audio_utils::CsdRecord> records;
+    float currentCsd;
+    {
+        std::lock_guard _l(mLock);
 
-    // only for internal callbacks
-    mMelAggregator->aggregateAndAddNewMelRecord(
-        audio_utils::MelRecord(deviceId, std::vector<float>(
-                                   mels.begin() + offset,
-                                   mels.begin() + offset + length),
-                               timestampSec - length));
+        int64_t timestampSec = getMonotonicSecond();
+
+        // only for internal callbacks
+        records = mMelAggregator->aggregateAndAddNewMelRecord(audio_utils::MelRecord(
+                deviceId, std::vector<float>(mels.begin() + offset, mels.begin() + offset + length),
+                timestampSec - length));
+
+        currentCsd = mMelAggregator->getCsd();
+    }
+
+    soundDoseCallback = getSoundDoseCallback();
+
+    if (records.size() > 0 && soundDoseCallback != nullptr) {
+        std::vector<media::SoundDoseRecord> newRecordsToReport;
+        for (const auto& record : records) {
+            newRecordsToReport.emplace_back(csdRecordToSoundDoseRecord(record));
+        }
+
+        soundDoseCallback->onNewCsdValue(currentCsd, newRecordsToReport);
+    }
 }
 
-void SoundDoseManager::onMomentaryExposure(float currentMel,
-                                           audio_port_handle_t deviceId) const {
-    ALOGV("%s: Momentary exposure for device %d triggered: %f MEL",
-          __func__,
-          deviceId,
-          currentMel);
+sp<media::ISoundDoseCallback> SoundDoseManager::getSoundDoseCallback() const {
+    std::lock_guard _l(mLock);
+    return mSoundDoseCallback;
+}
+
+void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const {
+    ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel);
 
     sp<media::ISoundDoseCallback> soundDoseCallback;
     {
@@ -142,15 +144,12 @@
     mSoundDoseCallback = callback;
 }
 
-std::string SoundDoseManager::dump() const
-{
+std::string SoundDoseManager::dump() const {
     std::string output;
     mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
         base::StringAppendF(&output,
                             "CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]",
-                            csdRecord.value,
-                            csdRecord.averageMel,
-                            csdRecord.timestamp,
+                            csdRecord.value, csdRecord.averageMel, csdRecord.timestamp,
                             csdRecord.timestamp + csdRecord.duration);
         base::StringAppendF(&output, "\n");
     });
@@ -173,4 +172,14 @@
     return mMelAggregator->getCachedMelRecordsSize();
 }
 
+media::SoundDoseRecord SoundDoseManager::csdRecordToSoundDoseRecord(
+        const audio_utils::CsdRecord& legacy) {
+    media::SoundDoseRecord soundDoseRecord{};
+    soundDoseRecord.timestamp = legacy.timestamp;
+    soundDoseRecord.duration = legacy.duration;
+    soundDoseRecord.value = legacy.value;
+    soundDoseRecord.averageMel = legacy.averageMel;
+    return soundDoseRecord;
+}
+
 }  // namespace android
diff --git a/services/audioflinger/sounddose/SoundDoseManager.h b/services/audioflinger/sounddose/SoundDoseManager.h
index 754b569..9a95e8a 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.h
+++ b/services/audioflinger/sounddose/SoundDoseManager.h
@@ -18,16 +18,16 @@
 #pragma once
 
 #include <android/media/ISoundDoseCallback.h>
-#include <audio_utils/MelProcessor.h>
 #include <audio_utils/MelAggregator.h>
+#include <audio_utils/MelProcessor.h>
+#include <utils/Errors.h>
 #include <mutex>
 #include <unordered_map>
-#include <utils/Errors.h>
 
 namespace android {
 
 class SoundDoseManager : public audio_utils::MelProcessor::MelCallback {
-public:
+  public:
     /** 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. */
@@ -35,7 +35,7 @@
 
     SoundDoseManager()
         : mMelAggregator(sp<audio_utils::MelAggregator>::make(kCsdWindowSeconds)),
-          mRs2Value(kDefaultRs2Value) {};
+          mRs2Value(kDefaultRs2Value){};
 
     /**
      * \brief Creates or gets the MelProcessor assigned to the streamHandle
@@ -48,12 +48,11 @@
      *
      * \return MelProcessor assigned to the stream and device id.
      */
-    sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(
-        audio_port_handle_t deviceId,
-        audio_io_handle_t streamHandle,
-        uint32_t sampleRate,
-        size_t channelCount,
-        audio_format_t format);
+    sp<audio_utils::MelProcessor> getOrCreateProcessorForDevice(audio_port_handle_t deviceId,
+                                                                audio_io_handle_t streamHandle,
+                                                                uint32_t sampleRate,
+                                                                size_t channelCount,
+                                                                audio_format_t format);
 
     /**
      * \brief Removes stream processor when MEL computation is not needed anymore
@@ -78,21 +77,25 @@
     // used for testing
     size_t getCachedMelRecordsSize() const;
 
+    /** Method for converting from audio_utils::CsdRecord to media::SoundDoseRecord. */
+    static media::SoundDoseRecord csdRecordToSoundDoseRecord(const audio_utils::CsdRecord& legacy);
+
     // ------ Override audio_utils::MelProcessor::MelCallback ------
-    void onNewMelValues(const std::vector<float>& mels,
-                        size_t offset,
-                        size_t length,
+    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:
+
+  private:
+    sp<media::ISoundDoseCallback> getSoundDoseCallback() const;
+
     mutable std::mutex mLock;
 
     // no need for lock since MelAggregator is thread-safe
     const sp<audio_utils::MelAggregator> mMelAggregator;
 
-    std::unordered_map<audio_io_handle_t,
-                       wp<audio_utils::MelProcessor>> mActiveProcessors GUARDED_BY(mLock);
+    std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors
+            GUARDED_BY(mLock);
 
     float mRs2Value GUARDED_BY(mLock);