/* * 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_NDEBUG 0 #define LOG_TAG "SoundDoseManager_tests" #include #include #include #include #include #include namespace android { namespace { using aidl::android::hardware::audio::core::sounddose::BnSoundDose; using aidl::android::media::audio::common::AudioDevice; using aidl::android::media::audio::common::AudioDeviceAddress; class HalSoundDoseMock : public BnSoundDose { public: MOCK_METHOD(ndk::ScopedAStatus, getOutputRs2UpperBound, (float*), (override)); MOCK_METHOD(ndk::ScopedAStatus, setOutputRs2UpperBound, (float), (override)); MOCK_METHOD(ndk::ScopedAStatus, registerSoundDoseCallback, (const std::shared_ptr&), (override)); }; class MelReporterCallback : public IMelReporterCallback { public: MOCK_METHOD(void, startMelComputationForDeviceId, (audio_port_handle_t), (override)); MOCK_METHOD(void, stopMelComputationForDeviceId, (audio_port_handle_t), (override)); MOCK_METHOD(void, applyAllAudioPatches, (), (override)); }; class MelAggregatorMock : public audio_utils::MelAggregator { public: MelAggregatorMock() : MelAggregator(100) {} MOCK_METHOD(std::vector, aggregateAndAddNewMelRecord, (const audio_utils::MelRecord&), (override)); }; constexpr char kPrimaryModule[] = "primary"; constexpr char kSecondaryModule[] = "secondary"; class SoundDoseManagerTest : public ::testing::Test { protected: void SetUp() override { mMelReporterCallback = sp::make(); mMelAggregator = sp::make(); mSoundDoseManager = sp::make(mMelReporterCallback, mMelAggregator); mHalSoundDose = ndk::SharedRefBase::make(); mSecondaryHalSoundDose = ndk::SharedRefBase::make(); ON_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound) .WillByDefault([] (float rs2) { EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2); return ndk::ScopedAStatus::ok(); }); ON_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound) .WillByDefault([] (float rs2) { EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2); return ndk::ScopedAStatus::ok(); }); } sp mMelReporterCallback; sp mMelAggregator; sp mSoundDoseManager; std::shared_ptr mHalSoundDose; std::shared_ptr mSecondaryHalSoundDose; }; TEST_F(SoundDoseManagerTest, GetProcessorForExistingStream) { sp processor1 = mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1, /*streamHandle=*/1, /*sampleRate*/44100, /*channelCount*/2, /*format*/AUDIO_FORMAT_PCM_FLOAT); sp processor2 = mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2, /*streamHandle=*/1, /*sampleRate*/44100, /*channelCount*/2, /*format*/AUDIO_FORMAT_PCM_FLOAT); EXPECT_EQ(processor1, processor2); } TEST_F(SoundDoseManagerTest, RemoveExistingStream) { sp processor1 = mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1, /*streamHandle=*/1, /*sampleRate*/44100, /*channelCount*/2, /*format*/AUDIO_FORMAT_PCM_FLOAT); mSoundDoseManager->removeStreamProcessor(1); sp processor2 = mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2, /*streamHandle=*/1, /*sampleRate*/44100, /*channelCount*/2, /*format*/AUDIO_FORMAT_PCM_FLOAT); EXPECT_NE(processor1, processor2); } TEST_F(SoundDoseManagerTest, NewMelValuesAttenuatedAggregateMels) { std::vectormels{1.f, 1.f}; EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord) .Times(1) .WillOnce([&] (const audio_utils::MelRecord& record) { EXPECT_THAT(record.mels, ::testing::ElementsAreArray(mels)); return std::vector(); }); mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1, /*attenuated=*/true); } TEST_F(SoundDoseManagerTest, NewMelValuesUnattenuatedAreSplit) { std::vectormels{79.f, 80.f, 79.f, 80.f, 79.f, 79.f, 80.f}; EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord) .Times(3) .WillRepeatedly([&] (const audio_utils::MelRecord& record) { EXPECT_EQ(record.mels.size(), size_t {1}); EXPECT_EQ(record.mels[0], 80.f); return std::vector(); }); mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1, /*attenuated=*/false); } TEST_F(SoundDoseManagerTest, InvalidHalInterfaceIsNotSet) { EXPECT_FALSE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, nullptr)); } TEST_F(SoundDoseManagerTest, SetHalSoundDoseDisablesNewMelProcessorCallbacks) { EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { EXPECT_NE(nullptr, callback); return ndk::ScopedAStatus::ok(); }); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, 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(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { EXPECT_NE(nullptr, callback); return ndk::ScopedAStatus::ok(); }); EXPECT_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mSecondaryHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { EXPECT_NE(nullptr, callback); return ndk::ScopedAStatus::ok(); }); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose)); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kSecondaryModule, mSecondaryHalSoundDose)); } TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalWithNoAddressIllegalArgument) { std::shared_ptr halCallback; EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { halCallback = callback; return ndk::ScopedAStatus::ok(); }); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose)); EXPECT_NE(nullptr, halCallback); AudioDevice audioDevice = {}; audioDevice.address.set("test"); auto status = halCallback->onMomentaryExposureWarning( /*in_currentDbA=*/101.f, audioDevice); EXPECT_FALSE(status.isOk()); } TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalAfterInternalSelectedReturnsException) { std::shared_ptr halCallback; EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { halCallback = callback; return ndk::ScopedAStatus::ok(); }); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose)); EXPECT_NE(nullptr, halCallback); mSoundDoseManager->resetHalSoundDoseInterfaces(); AudioDevice audioDevice = {}; audioDevice.address.set("test"); auto status = halCallback->onMomentaryExposureWarning( /*in_currentDbA=*/101.f, audioDevice); EXPECT_FALSE(status.isOk()); } TEST_F(SoundDoseManagerTest, OnNewMelValuesFromHalWithNoAddressIllegalArgument) { std::shared_ptr halCallback; EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1); EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback) .Times(1) .WillOnce([&] (const std::shared_ptr& callback) { halCallback = callback; return ndk::ScopedAStatus::ok(); }); EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose)); EXPECT_NE(nullptr, halCallback); AudioDevice audioDevice = {}; audioDevice.address.set("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 audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET; const AudioDeviceTypeAddr adt{deviceType, address}; auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice( deviceType, address.c_str()); ASSERT_TRUE(audioDevice.ok()); mSoundDoseManager->mapAddressToDeviceId(adt, deviceId); EXPECT_EQ(deviceId, mSoundDoseManager->getIdForAudioDevice(audioDevice.value())); } TEST_F(SoundDoseManagerTest, GetAfterClearIdReturnsNone) { const std::string address = "testAddress"; const audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET; const AudioDeviceTypeAddr adt{deviceType, address}; const audio_port_handle_t deviceId = 2; auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice( deviceType, address.c_str()); ASSERT_TRUE(audioDevice.ok()); mSoundDoseManager->mapAddressToDeviceId(adt, deviceId); mSoundDoseManager->clearMapDeviceIdEntries(deviceId); EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice.value())); } TEST_F(SoundDoseManagerTest, GetUnmappedIdReturnsHandleNone) { const std::string address = "testAddress"; AudioDevice audioDevice; audioDevice.address.set(address); EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice)); } TEST_F(SoundDoseManagerTest, GetDefaultForceComputeCsdOnAllDevices) { EXPECT_FALSE(mSoundDoseManager->isComputeCsdForcedOnAllDevices()); } TEST_F(SoundDoseManagerTest, GetDefaultForceUseFrameworkMel) { EXPECT_FALSE(mSoundDoseManager->isFrameworkMelForced()); } TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStopsNonHeadphone) { media::ISoundDose::AudioDeviceCategory device1; device1.address = "dev1"; device1.csdCompatible = false; device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address}; // this will mark the device as active mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1); EXPECT_CALL(*mMelReporterCallback.get(), stopMelComputationForDeviceId).Times(1); mSoundDoseManager->setAudioDeviceCategory(device1); } TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStartsHeadphone) { media::ISoundDose::AudioDeviceCategory device1; device1.address = "dev1"; device1.csdCompatible = true; device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address}; // this will mark the device as active mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1); EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1); mSoundDoseManager->setAudioDeviceCategory(device1); } TEST_F(SoundDoseManagerTest, InitCachedAudioDevicesStartsOnlyActiveDevices) { media::ISoundDose::AudioDeviceCategory device1; media::ISoundDose::AudioDeviceCategory device2; device1.address = "dev1"; device1.csdCompatible = true; device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; device2.address = "dev2"; device2.csdCompatible = true; device2.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address}; std::vector btDevices = {device1, device2}; // this will mark the device as active mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1); EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1); mSoundDoseManager->initCachedAudioDeviceCategories(btDevices); } } // namespace } // namespace android