Add default implementation and VTS for ISoundDose
The ISoundDose HAL interface is used for reporting the sound dose
relevant information from/to the HAL. This is necessary for all devices
that certify with the IEC62368-1 3rd edition and EN50332-3 standard
for safe hearing.
Bug: 248567177
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ib89e09243a01cebc2f7996b6b572384a1471867a
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index f2cebbf..b9b8cd8 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -43,6 +43,7 @@
"Configuration.cpp",
"EngineConfigXmlConverter.cpp",
"Module.cpp",
+ "SoundDose.cpp",
"Stream.cpp",
"Telephony.cpp",
],
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 2b199dc..86f0261 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -26,6 +26,7 @@
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include "core-impl/Module.h"
+#include "core-impl/SoundDose.h"
#include "core-impl/Telephony.h"
#include "core-impl/utils.h"
@@ -932,8 +933,11 @@
}
ndk::ScopedAStatus Module::getSoundDose(std::shared_ptr<ISoundDose>* _aidl_return) {
- *_aidl_return = nullptr;
- LOG(DEBUG) << __func__ << ": ISoundDose not implemented";
+ if (mSoundDose == nullptr) {
+ mSoundDose = ndk::SharedRefBase::make<SoundDose>();
+ }
+ *_aidl_return = mSoundDose;
+ LOG(DEBUG) << __func__ << ": returning instance of ISoundDose: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
diff --git a/audio/aidl/default/SoundDose.cpp b/audio/aidl/default/SoundDose.cpp
new file mode 100644
index 0000000..3d222a8
--- /dev/null
+++ b/audio/aidl/default/SoundDose.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "AHAL_SoundDose"
+
+#include "core-impl/SoundDose.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus SoundDose::setOutputRs2(float in_rs2ValueDbA) {
+ if (in_rs2ValueDbA < MIN_RS2 || in_rs2ValueDbA > DEFAULT_MAX_RS2) {
+ LOG(ERROR) << __func__ << ": RS2 value is invalid: " << in_rs2ValueDbA;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ mRs2Value = in_rs2ValueDbA;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SoundDose::getOutputRs2(float* _aidl_return) {
+ *_aidl_return = mRs2Value;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SoundDose::registerSoundDoseCallback(
+ const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) {
+ if (in_callback.get() == nullptr) {
+ LOG(ERROR) << __func__ << ": Callback is nullptr";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (mCallback != nullptr) {
+ LOG(ERROR) << __func__ << ": Sound dose callback was already registered";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ mCallback = in_callback;
+ LOG(DEBUG) << __func__ << ": Registered sound dose callback ";
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index de1fbe8..3cc31c5 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -124,6 +124,7 @@
bool mMasterMute = false;
float mMasterVolume = 1.0f;
bool mMicMute = false;
+ std::shared_ptr<ISoundDose> mSoundDose;
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h
new file mode 100644
index 0000000..54a6cbf
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/SoundDose.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include <aidl/android/hardware/audio/core/BnSoundDose.h>
+#include <aidl/android/media/audio/common/AudioDevice.h>
+
+using aidl::android::media::audio::common::AudioDevice;
+
+namespace aidl::android::hardware::audio::core {
+
+class SoundDose : public BnSoundDose {
+ public:
+ SoundDose() : mRs2Value(DEFAULT_MAX_RS2){};
+
+ ndk::ScopedAStatus setOutputRs2(float in_rs2ValueDbA) override;
+ ndk::ScopedAStatus getOutputRs2(float* _aidl_return) override;
+ ndk::ScopedAStatus registerSoundDoseCallback(
+ const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) override;
+
+ private:
+ std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback;
+ float mRs2Value;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 99771e6..5dadea3 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -36,6 +36,7 @@
#include <aidl/Vintf.h>
#include <aidl/android/hardware/audio/core/BnStreamCallback.h>
#include <aidl/android/hardware/audio/core/IModule.h>
+#include <aidl/android/hardware/audio/core/ISoundDose.h>
#include <aidl/android/hardware/audio/core/ITelephony.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
@@ -56,6 +57,7 @@
using aidl::android::hardware::audio::core::AudioPatch;
using aidl::android::hardware::audio::core::AudioRoute;
using aidl::android::hardware::audio::core::IModule;
+using aidl::android::hardware::audio::core::ISoundDose;
using aidl::android::hardware::audio::core::IStreamIn;
using aidl::android::hardware::audio::core::IStreamOut;
using aidl::android::hardware::audio::core::ITelephony;
@@ -2468,6 +2470,92 @@
}
}
+class AudioCoreSoundDose : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
+ public:
+ class NoOpHalSoundDoseCallback : public ISoundDose::BnHalSoundDoseCallback {
+ public:
+ ndk::ScopedAStatus onMomentaryExposureWarning(float in_currentDbA,
+ const AudioDevice& in_audioDevice) override;
+ ndk::ScopedAStatus onNewMelValues(
+ const ISoundDose::IHalSoundDoseCallback::MelRecord& in_melRecord,
+ const AudioDevice& in_audioDevice) override;
+ };
+
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam()));
+ ASSERT_IS_OK(module->getSoundDose(&soundDose));
+ callback = ndk::SharedRefBase::make<NoOpHalSoundDoseCallback>();
+ }
+
+ void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
+
+ std::shared_ptr<ISoundDose> soundDose;
+ std::shared_ptr<ISoundDose::IHalSoundDoseCallback> callback;
+};
+
+ndk::ScopedAStatus AudioCoreSoundDose::NoOpHalSoundDoseCallback::onMomentaryExposureWarning(
+ float in_currentDbA, const AudioDevice& in_audioDevice) {
+ // Do nothing
+ (void)in_currentDbA;
+ (void)in_audioDevice;
+ LOG(INFO) << "NoOpHalSoundDoseCallback::onMomentaryExposureWarning called";
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus AudioCoreSoundDose::NoOpHalSoundDoseCallback::onNewMelValues(
+ const ISoundDose::IHalSoundDoseCallback::MelRecord& in_melRecord,
+ const AudioDevice& in_audioDevice) {
+ // Do nothing
+ (void)in_melRecord;
+ (void)in_audioDevice;
+ LOG(INFO) << "NoOpHalSoundDoseCallback::onNewMelValues called";
+
+ return ndk::ScopedAStatus::ok();
+}
+
+TEST_P(AudioCoreSoundDose, GetSetOutputRs2) {
+ if (soundDose == nullptr) {
+ GTEST_SKIP() << "SoundDose is not supported";
+ }
+
+ bool isSupported = false;
+ EXPECT_NO_FATAL_FAILURE(TestAccessors<float>(soundDose.get(), &ISoundDose::getOutputRs2,
+ &ISoundDose::setOutputRs2,
+ /*validValues=*/{80.f, 90.f, 100.f},
+ /*invalidValues=*/{79.f, 101.f}, &isSupported));
+ EXPECT_TRUE(isSupported) << "Getting/Setting RS2 must be supported";
+}
+
+TEST_P(AudioCoreSoundDose, CheckDefaultRs2Value) {
+ if (soundDose == nullptr) {
+ GTEST_SKIP() << "SoundDose is not supported";
+ }
+
+ float rs2Value;
+ ASSERT_IS_OK(soundDose->getOutputRs2(&rs2Value));
+ EXPECT_EQ(rs2Value, ISoundDose::DEFAULT_MAX_RS2);
+}
+
+TEST_P(AudioCoreSoundDose, RegisterSoundDoseCallbackTwiceThrowsException) {
+ if (soundDose == nullptr) {
+ GTEST_SKIP() << "SoundDose is not supported";
+ }
+
+ ASSERT_IS_OK(soundDose->registerSoundDoseCallback(callback));
+ EXPECT_STATUS(EX_ILLEGAL_STATE, soundDose->registerSoundDoseCallback(callback))
+ << "Registering sound dose callback twice should throw EX_ILLEGAL_STATE";
+}
+
+TEST_P(AudioCoreSoundDose, RegisterSoundDoseNullCallbackThrowsException) {
+ if (soundDose == nullptr) {
+ GTEST_SKIP() << "SoundDose is not supported";
+ }
+
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, soundDose->registerSoundDoseCallback(nullptr))
+ << "Registering nullptr sound dose callback should throw EX_ILLEGAL_ARGUMENT";
+}
+
INSTANTIATE_TEST_SUITE_P(AudioCoreModuleTest, AudioCoreModule,
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
@@ -2484,6 +2572,10 @@
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
+INSTANTIATE_TEST_SUITE_P(AudioCoreSoundDoseTest, AudioCoreSoundDose,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+ android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreSoundDose);
// This is the value used in test sequences for which the test needs to ensure
// that the HAL stays in a transient state long enough to receive the next command.