CSD: add logic for using HAL sound dose values
When the HAL supports the sound dose computation we prefer to use the
MEL value provided by the HAL. These should be more accurate and can be
used for certification with IEC62368-1 3rd edition and EN50332-3.
Current implementation supports the standalone sound dose HAL.
Test: sounddosemanager_tests UT & bluejay-userdebug with internal MEL
Bug: 252776298
Change-Id: Ibbda76781fc869ac457d03d4f9a871fb353a9ba8
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 41d4e16..4b6d5f2 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -24,6 +24,7 @@
defaults: [
"latest_android_media_audio_common_types_cpp_shared",
+ "latest_android_hardware_audio_sounddose_ndk_shared",
],
srcs: [
@@ -76,6 +77,7 @@
"libutils",
"liblog",
"libbinder",
+ "libbinder_ndk",
"libaudioclient",
"libaudiomanager",
"libmedialogservice",
@@ -105,11 +107,11 @@
"libaudiohal_headers",
"libaudioutils_headers",
"libmedia_headers",
- "libsounddose_headers",
],
export_shared_lib_headers: [
"libpermission",
+ "android.hardware.audio.sounddose-V1-ndk",
],
cflags: [
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index d3453f5..9865dac 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2573,6 +2573,9 @@
ALOGE("loadHwModule() error %d loading module %s", rc, name);
return AUDIO_MODULE_HANDLE_NONE;
}
+ if (!mMelReporter->activateHalSoundDoseComputation(name)) {
+ ALOGW("loadHwModule() sound dose reporting is not available");
+ }
mHardwareStatus = AUDIO_HW_INIT;
rc = dev->initCheck();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6dd1cda..2f3ceeb 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <limits.h>
+#include <aidl/android/hardware/audio/sounddose/ISoundDoseFactory.h>
#include <android/media/BnAudioTrack.h>
#include <android/media/IAudioFlingerClient.h>
#include <android/media/IAudioTrackCallback.h>
diff --git a/services/audioflinger/MelReporter.cpp b/services/audioflinger/MelReporter.cpp
index b2e8027..7902444 100644
--- a/services/audioflinger/MelReporter.cpp
+++ b/services/audioflinger/MelReporter.cpp
@@ -22,12 +22,73 @@
#include <android/media/ISoundDoseCallback.h>
#include <audio_utils/power.h>
+#include <android/binder_manager.h>
#include <utils/Log.h>
+using aidl::android::hardware::audio::core::ISoundDose;
+using aidl::android::hardware::audio::sounddose::ISoundDoseFactory;
+
namespace android {
+constexpr std::string_view kSoundDoseInterfaceModule = "/default";
+
+bool AudioFlinger::MelReporter::activateHalSoundDoseComputation(const std::string& module) {
+ if (mSoundDoseFactory == nullptr) {
+ ALOGW("%s sound dose HAL reporting not available", __func__);
+ activateInternalSoundDoseComputation();
+ return false;
+ }
+
+ std::shared_ptr<ISoundDose> soundDoseInterface;
+ auto result = mSoundDoseFactory->getSoundDose(module, &soundDoseInterface);
+ if (!result.isOk()) {
+ ALOGW("%s HAL cannot provide sound dose interface for module %s",
+ __func__, module.c_str());
+ activateInternalSoundDoseComputation();
+ return false;
+ }
+
+ if (!mSoundDoseManager->setHalSoundDoseInterface(soundDoseInterface)) {
+ ALOGW("%s cannot activate HAL MEL reporting for module %s", __func__, module.c_str());
+ activateInternalSoundDoseComputation();
+ return false;
+ }
+
+ std::lock_guard _l(mLock);
+ mUseHalSoundDoseInterface = true;
+ stopInternalMelComputation();
+ return true;
+}
+
+void AudioFlinger::MelReporter::activateInternalSoundDoseComputation() {
+ mSoundDoseManager->setHalSoundDoseInterface(nullptr);
+
+ std::lock_guard _l(mLock);
+ mUseHalSoundDoseInterface = false;
+
+ for (const auto& activePatches : mActiveMelPatches) {
+ for (const auto& deviceId : activePatches.second.deviceHandles) {
+ startMelComputationForNewPatch(activePatches.second.streamHandle, deviceId);
+ }
+ }
+}
+
+void AudioFlinger::MelReporter::onFirstRef() {
+ mAudioFlinger.mPatchCommandThread->addListener(this);
+
+ std::string interface =
+ std::string(ISoundDoseFactory::descriptor) + kSoundDoseInterfaceModule.data();
+ AIBinder* binder = AServiceManager_checkService(interface.c_str());
+ if (binder == nullptr) {
+ ALOGW("%s service %s doesn't exist", __func__, interface.c_str());
+ return;
+ }
+
+ mSoundDoseFactory = ISoundDoseFactory::fromBinder(ndk::SpAIBinder(binder));
+}
+
bool AudioFlinger::MelReporter::shouldComputeMelForDeviceType(audio_devices_t device) {
- if (mSoundDoseManager.computeCsdOnAllDevices()) {
+ if (mSoundDoseManager->computeCsdOnAllDevices()) {
return true;
}
@@ -65,17 +126,17 @@
&& shouldComputeMelForDeviceType(patch.mAudioPatch.sinks[i].ext.device.type)) {
audio_port_handle_t deviceId = patch.mAudioPatch.sinks[i].id;
newPatch.deviceHandles.push_back(deviceId);
+ AudioDeviceTypeAddr adt{patch.mAudioPatch.sinks[i].ext.device.type,
+ patch.mAudioPatch.sinks[i].ext.device.address};
+ mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
- // Start the MEL calculation in the PlaybackThread
- std::lock_guard _lAf(mAudioFlinger.mLock);
- auto thread = mAudioFlinger.checkPlaybackThread_l(streamHandle);
- if (thread != nullptr) {
- thread->startMelComputation(mSoundDoseManager.getOrCreateProcessorForDevice(
- deviceId,
- newPatch.streamHandle,
- thread->mSampleRate,
- thread->mChannelCount,
- thread->mFormat));
+ bool useHalSoundDoseInterface;
+ {
+ std::lock_guard _l(mLock);
+ useHalSoundDoseInterface = mUseHalSoundDoseInterface;
+ }
+ if (!useHalSoundDoseInterface) {
+ startMelComputationForNewPatch(streamHandle, deviceId);
}
}
}
@@ -84,6 +145,21 @@
mActiveMelPatches[patch.mAudioPatch.id] = newPatch;
}
+void AudioFlinger::MelReporter::startMelComputationForNewPatch(
+ audio_io_handle_t streamHandle, audio_port_handle_t deviceId) {
+ // Start the MEL calculation in the PlaybackThread
+ std::lock_guard _lAf(mAudioFlinger.mLock);
+ auto thread = mAudioFlinger.checkPlaybackThread_l(streamHandle);
+ if (thread != nullptr) {
+ thread->startMelComputation(mSoundDoseManager->getOrCreateProcessorForDevice(
+ deviceId,
+ streamHandle,
+ thread->mSampleRate,
+ thread->mChannelCount,
+ thread->mFormat));
+ }
+}
+
void AudioFlinger::MelReporter::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
@@ -103,25 +179,47 @@
mActiveMelPatches.erase(patchIt);
}
- // 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();
+ for (const auto& deviceId : melPatch.deviceHandles) {
+ mSoundDoseManager->clearMapDeviceIdEntries(deviceId);
}
+ stopInternalMelComputationForStream(melPatch.streamHandle);
}
sp<media::ISoundDose> AudioFlinger::MelReporter::getSoundDoseInterface(
const sp<media::ISoundDoseCallback>& callback) {
// no need to lock since getSoundDoseInterface is synchronized
- return mSoundDoseManager.getSoundDoseInterface(callback);
+ return mSoundDoseManager->getSoundDoseInterface(callback);
+}
+
+void AudioFlinger::MelReporter::stopInternalMelComputation() {
+ ALOGV("%s", __func__);
+ std::unordered_map<audio_patch_handle_t, ActiveMelPatch> activePatchesCopy;
+ {
+ std::lock_guard _l(mLock);
+ activePatchesCopy = mActiveMelPatches;
+ mActiveMelPatches.clear();
+ }
+
+ for (const auto& activePatch : activePatchesCopy) {
+ stopInternalMelComputationForStream(activePatch.second.streamHandle);
+ }
+}
+
+void AudioFlinger::MelReporter::stopInternalMelComputationForStream(audio_io_handle_t streamId) {
+ ALOGV("%s: stop internal mel for stream id: %d", __func__, streamId);
+
+ std::lock_guard _lAf(mAudioFlinger.mLock);
+ mSoundDoseManager->removeStreamProcessor(streamId);
+ auto thread = mAudioFlinger.checkPlaybackThread_l(streamId);
+ if (thread != nullptr) {
+ thread->stopMelComputation();
+ }
}
std::string AudioFlinger::MelReporter::dump() {
std::lock_guard _l(mLock);
std::string output("\nSound Dose:\n");
- output.append(mSoundDoseManager.dump());
+ output.append(mSoundDoseManager->dump());
return output;
}
diff --git a/services/audioflinger/MelReporter.h b/services/audioflinger/MelReporter.h
index b1abc59..acbc8ed 100644
--- a/services/audioflinger/MelReporter.h
+++ b/services/audioflinger/MelReporter.h
@@ -32,17 +32,34 @@
class MelReporter : public PatchCommandThread::PatchCommandListener {
public:
explicit MelReporter(AudioFlinger& audioFlinger)
- : mAudioFlinger(audioFlinger) {}
+ : mAudioFlinger(audioFlinger),
+ mSoundDoseManager(sp<SoundDoseManager>::make()) {}
- void onFirstRef() override {
- mAudioFlinger.mPatchCommandThread->addListener(this);
- }
+ void onFirstRef() override;
/** Returns true if we should compute MEL for the given device. */
bool shouldComputeMelForDeviceType(audio_devices_t device);
- // For now only support internal MelReporting
- [[nodiscard]] bool isHalReportingEnabled() const { return false; }
+ /**
+ * Activates the MEL reporting from the HAL sound dose interface. If the HAL
+ * does not support the sound dose interface for this module, the internal MEL
+ * calculation will be use.
+ *
+ * For now support internal MelReporting only if the sound dose standalone HAL
+ * is not implemented
+ *
+ * @return true if the MEL reporting will be done from the sound dose HAL
+ * interface
+ */
+ bool activateHalSoundDoseComputation(const std::string& module);
+
+ /**
+ * Activates the MEL reporting from internal framework values. These are used
+ * as a fallback when there is no sound dose interface implementation from HAL.
+ * Note: the internal CSD computation does not guarantee a certification with
+ * IEC62368-1 3rd edition or EN50332-3
+ */
+ void activateInternalSoundDoseComputation();
sp<media::ISoundDose> getSoundDoseInterface(const sp<media::ISoundDoseCallback>& callback);
@@ -54,9 +71,17 @@
void onReleaseAudioPatch(audio_patch_handle_t handle) override;
private:
- AudioFlinger& mAudioFlinger; // does not own the object
+ void stopInternalMelComputation();
+ void stopInternalMelComputationForStream(audio_io_handle_t streamId);
- SoundDoseManager mSoundDoseManager;
+ void startMelComputationForNewPatch(audio_io_handle_t streamHandle,
+ audio_port_handle_t deviceId);
+
+ AudioFlinger& mAudioFlinger; // does not own the object
+ std::shared_ptr<::aidl::android::hardware::audio::sounddose::ISoundDoseFactory>
+ mSoundDoseFactory;
+
+ sp<SoundDoseManager> mSoundDoseManager;
struct ActiveMelPatch {
audio_io_handle_t streamHandle{AUDIO_IO_HANDLE_NONE};
@@ -70,4 +95,5 @@
std::mutex mLock;
std::unordered_map<audio_patch_handle_t, ActiveMelPatch>
mActiveMelPatches GUARDED_BY(AudioFlinger::MelReporter::mLock);
+ bool mUseHalSoundDoseInterface GUARDED_BY(AudioFlinger::MelReporter::mLock) = false;
};
diff --git a/services/audioflinger/sounddose/Android.bp b/services/audioflinger/sounddose/Android.bp
index 0e409d3..6d9a0cc 100644
--- a/services/audioflinger/sounddose/Android.bp
+++ b/services/audioflinger/sounddose/Android.bp
@@ -12,15 +12,24 @@
double_loadable: true,
+ defaults: [
+ "latest_android_media_audio_common_types_ndk_shared",
+ "latest_android_hardware_audio_core_sounddose_ndk_shared",
+ "latest_android_hardware_audio_sounddose_ndk_shared",
+ ],
+
srcs: [
"SoundDoseManager.cpp",
],
shared_libs: [
"audioflinger-aidl-cpp",
+ "libaudio_aidl_conversion_common_ndk",
+ "libaudiofoundation",
"libaudioutils",
"libbase",
"libbinder",
+ "libbinder_ndk",
"liblog",
"libutils",
],
diff --git a/services/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 61f27cb..ad19fb1 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -20,14 +20,22 @@
#include "SoundDoseManager.h"
+#if !defined(BACKEND_NDK)
+#define BACKEND_NDK
+#endif
+
+#include "android/media/SoundDoseRecord.h"
#include <android-base/stringprintf.h>
+#include <media/AidlConversionCppNdk.h>
+#include <cinttypes>
#include <time.h>
#include <utils/Log.h>
-#include <cinttypes>
-#include "android/media/SoundDoseRecord.h"
namespace android {
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+
namespace {
int64_t getMonotonicSecond() {
@@ -46,6 +54,11 @@
size_t channelCount, audio_format_t format) {
std::lock_guard _l(mLock);
+ if (mHalSoundDose != nullptr) {
+ ALOGW("%s: using HAL MEL computation, no MelProcessor needed.", __func__);
+ return nullptr;
+ }
+
auto streamProcessor = mActiveProcessors.find(streamHandle);
sp<audio_utils::MelProcessor> processor;
if (streamProcessor != mActiveProcessors.end() &&
@@ -63,11 +76,54 @@
}
}
+bool SoundDoseManager::setHalSoundDoseInterface(const std::shared_ptr<ISoundDose>& halSoundDose) {
+ ALOGV("%s", __func__);
+
+ {
+ std::lock_guard _l(mLock);
+
+ mHalSoundDose = halSoundDose;
+ if (halSoundDose == nullptr) {
+ ALOGI("%s: passed ISoundDose object is null, switching to internal CSD", __func__);
+ return false;
+ }
+
+ if (!mHalSoundDose->setOutputRs2(mRs2Value).isOk()) {
+ ALOGW("%s: Cannot set RS2 value for momentary exposure %f",
+ __func__,
+ mRs2Value);
+ }
+
+ // initialize the HAL sound dose callback lazily
+ if (mHalSoundDoseCallback == nullptr) {
+ mHalSoundDoseCallback =
+ ndk::SharedRefBase::make<HalSoundDoseCallback>(this);
+ }
+ }
+
+ auto status = halSoundDose->registerSoundDoseCallback(mHalSoundDoseCallback);
+ if (!status.isOk()) {
+ // Not a warning since this can happen if the callback was registered before
+ ALOGI("%s: Cannot register HAL sound dose callback with status message: %s",
+ __func__,
+ status.getMessage());
+ }
+
+ return true;
+}
+
void SoundDoseManager::setOutputRs2(float rs2Value) {
ALOGV("%s", __func__);
std::lock_guard _l(mLock);
mRs2Value = rs2Value;
+ if (mHalSoundDose != nullptr) {
+ // using the HAL sound dose interface
+ if (!mHalSoundDose->setOutputRs2(mRs2Value).isOk()) {
+ ALOGE("%s: Cannot set RS2 value for momentary exposure %f", __func__, mRs2Value);
+ }
+ return;
+ }
for (auto& streamProcessor : mActiveProcessors) {
sp<audio_utils::MelProcessor> processor = streamProcessor.second.promote();
@@ -89,6 +145,88 @@
}
}
+audio_port_handle_t SoundDoseManager::getIdForAudioDevice(const AudioDevice& audioDevice) const {
+ std::lock_guard _l(mLock);
+
+ audio_devices_t type;
+ std::string address;
+ auto result = aidl::android::aidl2legacy_AudioDevice_audio_device(
+ audioDevice, &type, &address);
+ if (result != NO_ERROR) {
+ ALOGE("%s: could not convert from AudioDevice to AudioDeviceTypeAddr", __func__);
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+
+ auto adt = AudioDeviceTypeAddr(type, address);
+ auto deviceIt = mActiveDevices.find(adt);
+ if (deviceIt == mActiveDevices.end()) {
+ ALOGE("%s: could not find port id for device %s", __func__, adt.toString().c_str());
+ return AUDIO_PORT_HANDLE_NONE;
+ }
+ return deviceIt->second;
+}
+
+void SoundDoseManager::mapAddressToDeviceId(const AudioDeviceTypeAddr& adt,
+ const audio_port_handle_t deviceId) {
+ std::lock_guard _l(mLock);
+ ALOGI("%s: map address: %s to device id: %d", __func__, adt.toString().c_str(), deviceId);
+ mActiveDevices[adt] = deviceId;
+}
+
+void SoundDoseManager::clearMapDeviceIdEntries(audio_port_handle_t deviceId) {
+ std::lock_guard _l(mLock);
+ for (auto activeDevice = mActiveDevices.begin(); activeDevice != mActiveDevices.end();) {
+ if (activeDevice->second == deviceId) {
+ ALOGI("%s: clear mapping addr: %s to deviceId: %d",
+ __func__, activeDevice->first.toString().c_str(), deviceId);
+ activeDevice = mActiveDevices.erase(activeDevice);
+ continue;
+ }
+ ++activeDevice;
+ }
+ return;
+}
+
+ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onMomentaryExposureWarning(
+ float in_currentDbA, const AudioDevice& in_audioDevice) {
+ auto soundDoseManager = mSoundDoseManager.promote();
+ if (soundDoseManager == nullptr) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ auto id = soundDoseManager->getIdForAudioDevice(in_audioDevice);
+ if (id == AUDIO_PORT_HANDLE_NONE) {
+ ALOGW("%s: no mapped id for audio device with type %d and address %s",
+ __func__, in_audioDevice.type.type,
+ in_audioDevice.address.get<AudioDeviceAddress::id>().c_str());
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ soundDoseManager->onMomentaryExposure(in_currentDbA, id);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onNewMelValues(
+ const ISoundDose::IHalSoundDoseCallback::MelRecord& in_melRecord,
+ const AudioDevice& in_audioDevice) {
+ auto soundDoseManager = mSoundDoseManager.promote();
+ if (soundDoseManager == nullptr) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto id = soundDoseManager->getIdForAudioDevice(in_audioDevice);
+ if (id == AUDIO_PORT_HANDLE_NONE) {
+ ALOGW("%s: no mapped id for audio device with type %d and address %s",
+ __func__, in_audioDevice.type.type,
+ in_audioDevice.address.get<AudioDeviceAddress::id>().c_str());
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ // TODO: introduce timestamp in onNewMelValues callback
+ soundDoseManager->onNewMelValues(in_melRecord.melValues, 0,
+ in_melRecord.melValues.size(), id);
+
+ return ndk::ScopedAStatus::ok();
+}
+
void SoundDoseManager::SoundDose::binderDied(__unused const wp<IBinder>& who) {
ALOGV("%s", __func__);
diff --git a/services/audioflinger/sounddose/SoundDoseManager.h b/services/audioflinger/sounddose/SoundDoseManager.h
index eb5fa49..199e8c9 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.h
+++ b/services/audioflinger/sounddose/SoundDoseManager.h
@@ -17,8 +17,11 @@
#pragma once
+#include <aidl/android/hardware/audio/core/ISoundDose.h>
+#include <aidl/android/media/audio/common/AudioDevice.h>
#include <android/media/BnSoundDose.h>
#include <android/media/ISoundDoseCallback.h>
+#include <media/AudioDeviceTypeAddr.h>
#include <audio_utils/MelAggregator.h>
#include <audio_utils/MelProcessor.h>
#include <binder/Status.h>
@@ -27,8 +30,10 @@
namespace android {
+using aidl::android::hardware::audio::core::ISoundDose;
+
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. */
@@ -36,13 +41,13 @@
SoundDoseManager()
: mMelAggregator(sp<audio_utils::MelAggregator>::make(kCsdWindowSeconds)),
- mRs2Value(kDefaultRs2Value){};
+ mRs2Value(kDefaultRs2Value) {};
/**
* \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 streamHandle 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.
@@ -58,7 +63,7 @@
/**
* \brief Removes stream processor when MEL computation is not needed anymore
*
- * \param streanHandle handle to the stream
+ * \param streamHandle handle to the stream
*/
void removeStreamProcessor(audio_io_handle_t streamHandle);
@@ -78,6 +83,25 @@
**/
sp<media::ISoundDose> getSoundDoseInterface(const sp<media::ISoundDoseCallback>& callback);
+ /**
+ * Sets the HAL sound dose interface to use for the MEL computation. Use nullptr
+ * for using the internal MEL computation.
+ *
+ * @return true if setting the HAL sound dose value was successful, false otherwise.
+ */
+ bool setHalSoundDoseInterface(const std::shared_ptr<ISoundDose>& halSoundDose);
+
+ /** Returns the cached audio port id from the active devices. */
+ audio_port_handle_t getIdForAudioDevice(
+ const aidl::android::media::audio::common::AudioDevice& audioDevice) const;
+
+ /** Caches mapping between address and device port id. */
+ void mapAddressToDeviceId(const AudioDeviceTypeAddr& adt,
+ const audio_port_handle_t deviceId);
+
+ /** Clear all map entries with passed audio_port_handle_t. */
+ void clearMapDeviceIdEntries(audio_port_handle_t deviceId);
+
std::string dump() const;
// used for testing
@@ -101,7 +125,7 @@
public:
SoundDose(SoundDoseManager* manager, const sp<media::ISoundDoseCallback>& callback)
: mSoundDoseManager(manager),
- mSoundDoseCallback(callback) {};
+ mSoundDoseCallback(callback) {}
/** IBinder::DeathRecipient. Listen to the death of ISoundDoseCallback. */
virtual void binderDied(const wp<IBinder>& who);
@@ -119,6 +143,21 @@
const sp<media::ISoundDoseCallback> mSoundDoseCallback;
};
+ class HalSoundDoseCallback : public ISoundDose::BnHalSoundDoseCallback {
+ public:
+ explicit HalSoundDoseCallback(SoundDoseManager* manager)
+ : mSoundDoseManager(manager) {}
+
+ ndk::ScopedAStatus onMomentaryExposureWarning(
+ float in_currentDbA,
+ const aidl::android::media::audio::common::AudioDevice& in_audioDevice) override;
+ ndk::ScopedAStatus onNewMelValues(
+ const ISoundDose::IHalSoundDoseCallback::MelRecord& in_melRecord,
+ const aidl::android::media::audio::common::AudioDevice& in_audioDevice) override;
+
+ wp<SoundDoseManager> mSoundDoseManager;
+ };
+
void resetSoundDose();
void resetCsd(float currentCsd, const std::vector<media::SoundDoseRecord>& records);
@@ -136,10 +175,16 @@
std::unordered_map<audio_io_handle_t, wp<audio_utils::MelProcessor>> mActiveProcessors
GUARDED_BY(mLock);
+ // map active device address and type to device id
+ std::map<AudioDeviceTypeAddr, audio_port_handle_t> mActiveDevices GUARDED_BY(mLock);
+
float mRs2Value GUARDED_BY(mLock);
sp<SoundDose> mSoundDose GUARDED_BY(mLock);
+ std::shared_ptr<ISoundDose> mHalSoundDose GUARDED_BY(mLock);
+ std::shared_ptr<HalSoundDoseCallback> mHalSoundDoseCallback GUARDED_BY(mLock);
+
bool mUseFrameworkMel GUARDED_BY(mLock);
bool mComputeCsdOnAllDevices GUARDED_BY(mLock);
};
diff --git a/services/audioflinger/sounddose/tests/Android.bp b/services/audioflinger/sounddose/tests/Android.bp
index a886663..fef73dc 100644
--- a/services/audioflinger/sounddose/tests/Android.bp
+++ b/services/audioflinger/sounddose/tests/Android.bp
@@ -14,15 +14,24 @@
"sounddosemanager_tests.cpp"
],
+ defaults: [
+ "latest_android_media_audio_common_types_ndk_static",
+ "latest_android_hardware_audio_core_sounddose_ndk_static",
+ "latest_android_hardware_audio_sounddose_ndk_static",
+ ],
+
shared_libs: [
"audioflinger-aidl-cpp",
+ "libaudiofoundation",
"libaudioutils",
"libbase",
+ "libbinder_ndk",
"liblog",
"libutils",
],
static_libs: [
+ "libaudio_aidl_conversion_common_ndk",
"libgmock",
"libsounddose",
],
@@ -37,4 +46,8 @@
"-Werror",
"-Wextra",
],
+
+ test_suites: [
+ "general-tests",
+ ],
}
\ No newline at end of file
diff --git a/services/audioflinger/sounddose/tests/TEST_MAPPING b/services/audioflinger/sounddose/tests/TEST_MAPPING
new file mode 100644
index 0000000..dd7fe4d
--- /dev/null
+++ b/services/audioflinger/sounddose/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "sounddosemanager_tests"
+ }
+ ]
+}
diff --git a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
index 0aa5a20..658efe1 100644
--- a/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
+++ b/services/audioflinger/sounddose/tests/sounddosemanager_tests.cpp
@@ -15,25 +15,55 @@
*/
// #define LOG_NDEBUG 0
-#define LOG_TAG "sounddosemanager_tests"
+#define LOG_TAG "SoundDoseManager_tests"
+
+#include <aidl/android/hardware/audio/core/BnSoundDose.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include <SoundDoseManager.h>
-#include <gtest/gtest.h>
-
namespace android {
namespace {
-TEST(SoundDoseManagerTest, GetProcessorForExistingStream) {
- SoundDoseManager soundDoseManager;
+using aidl::android::hardware::audio::core::BnSoundDose;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+
+class HalSoundDoseMock : public BnSoundDose {
+public:
+ MOCK_METHOD(ndk::ScopedAStatus, getOutputRs2, (float*), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setOutputRs2, (float), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, registerSoundDoseCallback,
+ (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>&), (override));
+};
+
+class SoundDoseManagerTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mSoundDoseManager = sp<SoundDoseManager>::make();
+ mHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
+
+ ON_CALL(*mHalSoundDose.get(), setOutputRs2)
+ .WillByDefault([] (float rs2) {
+ EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2);
+ return ndk::ScopedAStatus::ok();
+ });
+ }
+
+ sp<SoundDoseManager> mSoundDoseManager;
+ std::shared_ptr<HalSoundDoseMock> mHalSoundDose;
+};
+
+TEST_F(SoundDoseManagerTest, GetProcessorForExistingStream) {
sp<audio_utils::MelProcessor> processor1 =
- soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+ mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
/*streamHandle=*/1,
/*sampleRate*/44100,
/*channelCount*/2,
/*format*/AUDIO_FORMAT_PCM_FLOAT);
sp<audio_utils::MelProcessor> processor2 =
- soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+ mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
/*streamHandle=*/1,
/*sampleRate*/44100,
/*channelCount*/2,
@@ -42,18 +72,17 @@
EXPECT_EQ(processor1, processor2);
}
-TEST(SoundDoseManagerTest, RemoveExistingStream) {
- SoundDoseManager soundDoseManager;
+TEST_F(SoundDoseManagerTest, RemoveExistingStream) {
sp<audio_utils::MelProcessor> processor1 =
- soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/1,
+ mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
/*streamHandle=*/1,
/*sampleRate*/44100,
/*channelCount*/2,
/*format*/AUDIO_FORMAT_PCM_FLOAT);
- soundDoseManager.removeStreamProcessor(1);
+ mSoundDoseManager->removeStreamProcessor(1);
sp<audio_utils::MelProcessor> processor2 =
- soundDoseManager.getOrCreateProcessorForDevice(/*deviceId=*/2,
+ mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
/*streamHandle=*/1,
/*sampleRate*/44100,
/*channelCount*/2,
@@ -62,13 +91,120 @@
EXPECT_NE(processor1, processor2);
}
-TEST(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
- SoundDoseManager soundDoseManager;
+TEST_F(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
std::vector<float>mels{1, 1};
- soundDoseManager.onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
+ mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
- EXPECT_EQ(soundDoseManager.getCachedMelRecordsSize(), size_t{1});
+ EXPECT_EQ(mSoundDoseManager->getCachedMelRecordsSize(), size_t{1});
+}
+
+TEST_F(SoundDoseManagerTest, InvalidHalInterfaceIsNotSet) {
+ EXPECT_FALSE(mSoundDoseManager->setHalSoundDoseInterface(nullptr));
+}
+
+TEST_F(SoundDoseManagerTest, SetHalSoundDoseDisablesNewMelProcessorCallbacks) {
+ EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2).Times(1);
+ EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
+ .Times(1)
+ .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
+ EXPECT_NE(nullptr, callback);
+ return ndk::ScopedAStatus::ok();
+ });
+
+ EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(mHalSoundDose));
+
+ EXPECT_EQ(nullptr, mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
+ /*streamHandle=*/1,
+ /*sampleRate*/44100,
+ /*channelCount*/2,
+ /*format*/AUDIO_FORMAT_PCM_FLOAT));
+}
+
+TEST_F(SoundDoseManagerTest, SetHalSoundDoseRegistersHalCallbacks) {
+ EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2).Times(1);
+ EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
+ .Times(1)
+ .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
+ EXPECT_NE(nullptr, callback);
+ return ndk::ScopedAStatus::ok();
+ });
+
+ EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(mHalSoundDose));
+}
+
+TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalWithNoAddressIllegalArgument) {
+ std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
+
+ EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2).Times(1);
+ EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
+ .Times(1)
+ .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
+ halCallback = callback;
+ return ndk::ScopedAStatus::ok();
+ });
+
+ EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(mHalSoundDose));
+
+ EXPECT_NE(nullptr, halCallback);
+ AudioDevice audioDevice = {};
+ audioDevice.address.set<AudioDeviceAddress::id>("test");
+ auto status = halCallback->onMomentaryExposureWarning(
+ /*in_currentDbA=*/101.f, audioDevice);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(SoundDoseManagerTest, OnNewMelValuesFromHalWithNoAddressIllegalArgument) {
+ std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
+
+ EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2).Times(1);
+ EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
+ .Times(1)
+ .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
+ halCallback = callback;
+ return ndk::ScopedAStatus::ok();
+ });
+
+ EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(mHalSoundDose));
+
+ EXPECT_NE(nullptr, halCallback);
+ AudioDevice audioDevice = {};
+ audioDevice.address.set<AudioDeviceAddress::id>("test");
+ auto status = halCallback->onNewMelValues(/*in_melRecord=*/{}, audioDevice);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(SoundDoseManagerTest, GetIdReturnsMappedAddress) {
+ const std::string address = "testAddress";
+ const audio_port_handle_t deviceId = 2;
+ const AudioDeviceTypeAddr adt{audio_devices_t{0}, address};
+ AudioDevice audioDevice;
+ audioDevice.address.set<AudioDeviceAddress::id>(address);
+
+ mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
+
+ EXPECT_EQ(deviceId, mSoundDoseManager->getIdForAudioDevice(audioDevice));
+}
+
+TEST_F(SoundDoseManagerTest, GetAfterClearIdReturnsNone) {
+ const std::string address = "testAddress";
+ const AudioDeviceTypeAddr adt {audio_devices_t{0}, address};
+ const audio_port_handle_t deviceId = 2;
+ AudioDevice audioDevice;
+ audioDevice.address.set<AudioDeviceAddress::id>(address);
+
+ mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
+ mSoundDoseManager->clearMapDeviceIdEntries(deviceId);
+
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice));
+}
+
+TEST_F(SoundDoseManagerTest, GetUnmappedIdReturnsHandleNone) {
+ const std::string address = "testAddress";
+ AudioDevice audioDevice;
+ audioDevice.address.set<AudioDeviceAddress::id>(address);
+
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice));
}
} // namespace