1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // #define LOG_NDEBUG 0
18 #define LOG_TAG "SoundDoseManager_tests"
19 
20 #include <SoundDoseManager.h>
21 
22 #include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
23 #include <audio_utils/MelAggregator.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <media/AidlConversionCppNdk.h>
27 
28 namespace android {
29 namespace {
30 
31 using aidl::android::hardware::audio::core::sounddose::BnSoundDose;
32 using aidl::android::media::audio::common::AudioDevice;
33 using aidl::android::media::audio::common::AudioDeviceAddress;
34 
35 class HalSoundDoseMock : public BnSoundDose {
36 public:
37     MOCK_METHOD(ndk::ScopedAStatus, getOutputRs2UpperBound, (float*), (override));
38     MOCK_METHOD(ndk::ScopedAStatus, setOutputRs2UpperBound, (float), (override));
39     MOCK_METHOD(ndk::ScopedAStatus, registerSoundDoseCallback,
40                 (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>&), (override));
41 };
42 
43 class MelReporterCallback : public IMelReporterCallback {
44 public:
45     MOCK_METHOD(void, startMelComputationForDeviceId, (audio_port_handle_t), (override));
46     MOCK_METHOD(void, stopMelComputationForDeviceId, (audio_port_handle_t), (override));
47     MOCK_METHOD(void, applyAllAudioPatches, (), (override));
48 };
49 
50 class MelAggregatorMock : public audio_utils::MelAggregator {
51 public:
MelAggregatorMock()52     MelAggregatorMock() : MelAggregator(100) {}
53 
54     MOCK_METHOD(std::vector<audio_utils::CsdRecord>, aggregateAndAddNewMelRecord,
55                 (const audio_utils::MelRecord&), (override));
56 };
57 
58 constexpr char kPrimaryModule[] = "primary";
59 constexpr char kSecondaryModule[] = "secondary";
60 
61 class SoundDoseManagerTest : public ::testing::Test {
62 protected:
SetUp()63     void SetUp() override {
64         mMelReporterCallback = sp<MelReporterCallback>::make();
65         mMelAggregator = sp<MelAggregatorMock>::make();
66         mSoundDoseManager = sp<SoundDoseManager>::make(mMelReporterCallback, mMelAggregator);
67         mHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
68         mSecondaryHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
69 
70         ON_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound)
71             .WillByDefault([] (float rs2) {
72                 EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2);
73                 return ndk::ScopedAStatus::ok();
74             });
75         ON_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound)
76                 .WillByDefault([] (float rs2) {
77                     EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2);
78                     return ndk::ScopedAStatus::ok();
79                 });
80     }
81 
82     sp<MelReporterCallback> mMelReporterCallback;
83     sp<MelAggregatorMock> mMelAggregator;
84     sp<SoundDoseManager> mSoundDoseManager;
85     std::shared_ptr<HalSoundDoseMock> mHalSoundDose;
86     std::shared_ptr<HalSoundDoseMock> mSecondaryHalSoundDose;
87 };
88 
TEST_F(SoundDoseManagerTest,GetProcessorForExistingStream)89 TEST_F(SoundDoseManagerTest, GetProcessorForExistingStream) {
90     sp<audio_utils::MelProcessor> processor1 =
91         mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
92             /*streamHandle=*/1,
93             /*sampleRate*/44100,
94             /*channelCount*/2,
95             /*format*/AUDIO_FORMAT_PCM_FLOAT);
96     sp<audio_utils::MelProcessor> processor2 =
97         mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
98             /*streamHandle=*/1,
99             /*sampleRate*/44100,
100             /*channelCount*/2,
101             /*format*/AUDIO_FORMAT_PCM_FLOAT);
102 
103     EXPECT_EQ(processor1, processor2);
104 }
105 
TEST_F(SoundDoseManagerTest,RemoveExistingStream)106 TEST_F(SoundDoseManagerTest, RemoveExistingStream) {
107     sp<audio_utils::MelProcessor> processor1 =
108         mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
109             /*streamHandle=*/1,
110             /*sampleRate*/44100,
111             /*channelCount*/2,
112             /*format*/AUDIO_FORMAT_PCM_FLOAT);
113 
114     mSoundDoseManager->removeStreamProcessor(1);
115     sp<audio_utils::MelProcessor> processor2 =
116         mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
117             /*streamHandle=*/1,
118             /*sampleRate*/44100,
119             /*channelCount*/2,
120             /*format*/AUDIO_FORMAT_PCM_FLOAT);
121 
122     EXPECT_NE(processor1, processor2);
123 }
124 
TEST_F(SoundDoseManagerTest,NewMelValuesAttenuatedAggregateMels)125 TEST_F(SoundDoseManagerTest, NewMelValuesAttenuatedAggregateMels) {
126     std::vector<float>mels{1.f, 1.f};
127 
128     EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord)
129             .Times(1)
130             .WillOnce([&] (const audio_utils::MelRecord& record) {
131                 EXPECT_THAT(record.mels, ::testing::ElementsAreArray(mels));
132                 return std::vector<audio_utils::CsdRecord>();
133             });
134 
135     mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1,
136                                       /*attenuated=*/true);
137 }
138 
TEST_F(SoundDoseManagerTest,NewMelValuesUnattenuatedAreSplit)139 TEST_F(SoundDoseManagerTest, NewMelValuesUnattenuatedAreSplit) {
140     std::vector<float>mels{79.f, 80.f, 79.f, 80.f, 79.f, 79.f, 80.f};
141 
142     EXPECT_CALL(*mMelAggregator.get(), aggregateAndAddNewMelRecord)
143             .Times(3)
144             .WillRepeatedly([&] (const audio_utils::MelRecord& record) {
145                 EXPECT_EQ(record.mels.size(), size_t {1});
146                 EXPECT_EQ(record.mels[0], 80.f);
147                 return std::vector<audio_utils::CsdRecord>();
148             });
149 
150     mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1,
151             /*attenuated=*/false);
152 }
153 
TEST_F(SoundDoseManagerTest,InvalidHalInterfaceIsNotSet)154 TEST_F(SoundDoseManagerTest, InvalidHalInterfaceIsNotSet) {
155     EXPECT_FALSE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, nullptr));
156 }
157 
TEST_F(SoundDoseManagerTest,SetHalSoundDoseDisablesNewMelProcessorCallbacks)158 TEST_F(SoundDoseManagerTest, SetHalSoundDoseDisablesNewMelProcessorCallbacks) {
159     EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
160     EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
161         .Times(1)
162         .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
163             EXPECT_NE(nullptr, callback);
164             return ndk::ScopedAStatus::ok();
165         });
166 
167     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
168 
169     EXPECT_EQ(nullptr, mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
170             /*streamHandle=*/1,
171             /*sampleRate*/44100,
172             /*channelCount*/2,
173             /*format*/AUDIO_FORMAT_PCM_FLOAT));
174 }
175 
TEST_F(SoundDoseManagerTest,SetHalSoundDoseRegistersHalCallbacks)176 TEST_F(SoundDoseManagerTest, SetHalSoundDoseRegistersHalCallbacks) {
177     EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
178     EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
179         .Times(1)
180         .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
181             EXPECT_NE(nullptr, callback);
182             return ndk::ScopedAStatus::ok();
183         });
184     EXPECT_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
185     EXPECT_CALL(*mSecondaryHalSoundDose.get(), registerSoundDoseCallback)
186             .Times(1)
187             .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
188                 EXPECT_NE(nullptr, callback);
189                 return ndk::ScopedAStatus::ok();
190         });
191 
192     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
193     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kSecondaryModule,
194                                                             mSecondaryHalSoundDose));
195 }
196 
TEST_F(SoundDoseManagerTest,MomentaryExposureFromHalWithNoAddressIllegalArgument)197 TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalWithNoAddressIllegalArgument) {
198     std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
199 
200     EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
201     EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
202        .Times(1)
203        .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
204            halCallback = callback;
205            return ndk::ScopedAStatus::ok();
206        });
207 
208     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
209 
210     EXPECT_NE(nullptr, halCallback);
211     AudioDevice audioDevice = {};
212     audioDevice.address.set<AudioDeviceAddress::id>("test");
213     auto status = halCallback->onMomentaryExposureWarning(
214         /*in_currentDbA=*/101.f, audioDevice);
215     EXPECT_FALSE(status.isOk());
216 }
217 
TEST_F(SoundDoseManagerTest,MomentaryExposureFromHalAfterInternalSelectedReturnsException)218 TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalAfterInternalSelectedReturnsException) {
219     std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
220 
221     EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
222     EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
223        .Times(1)
224        .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
225            halCallback = callback;
226            return ndk::ScopedAStatus::ok();
227        });
228 
229     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
230     EXPECT_NE(nullptr, halCallback);
231     mSoundDoseManager->resetHalSoundDoseInterfaces();
232 
233     AudioDevice audioDevice = {};
234     audioDevice.address.set<AudioDeviceAddress::id>("test");
235     auto status = halCallback->onMomentaryExposureWarning(
236         /*in_currentDbA=*/101.f, audioDevice);
237     EXPECT_FALSE(status.isOk());
238 }
239 
TEST_F(SoundDoseManagerTest,OnNewMelValuesFromHalWithNoAddressIllegalArgument)240 TEST_F(SoundDoseManagerTest, OnNewMelValuesFromHalWithNoAddressIllegalArgument) {
241     std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
242 
243     EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
244     EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
245        .Times(1)
246        .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
247            halCallback = callback;
248            return ndk::ScopedAStatus::ok();
249        });
250 
251     EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
252 
253     EXPECT_NE(nullptr, halCallback);
254     AudioDevice audioDevice = {};
255     audioDevice.address.set<AudioDeviceAddress::id>("test");
256     auto status = halCallback->onNewMelValues(/*in_melRecord=*/{}, audioDevice);
257     EXPECT_FALSE(status.isOk());
258 }
259 
TEST_F(SoundDoseManagerTest,GetIdReturnsMappedAddress)260 TEST_F(SoundDoseManagerTest, GetIdReturnsMappedAddress) {
261     const std::string address = "testAddress";
262     const audio_port_handle_t deviceId = 2;
263     const audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET;
264     const AudioDeviceTypeAddr adt{deviceType, address};
265     auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice(
266             deviceType, address.c_str());
267     ASSERT_TRUE(audioDevice.ok());
268 
269     mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
270 
271     EXPECT_EQ(deviceId, mSoundDoseManager->getIdForAudioDevice(audioDevice.value()));
272 }
273 
TEST_F(SoundDoseManagerTest,GetAfterClearIdReturnsNone)274 TEST_F(SoundDoseManagerTest, GetAfterClearIdReturnsNone) {
275     const std::string address = "testAddress";
276     const audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET;
277     const AudioDeviceTypeAddr adt{deviceType, address};
278     const audio_port_handle_t deviceId = 2;
279     auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice(
280             deviceType, address.c_str());
281     ASSERT_TRUE(audioDevice.ok());
282 
283     mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
284     mSoundDoseManager->clearMapDeviceIdEntries(deviceId);
285 
286     EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice.value()));
287 }
288 
TEST_F(SoundDoseManagerTest,GetUnmappedIdReturnsHandleNone)289 TEST_F(SoundDoseManagerTest, GetUnmappedIdReturnsHandleNone) {
290     const std::string address = "testAddress";
291     AudioDevice audioDevice;
292     audioDevice.address.set<AudioDeviceAddress::id>(address);
293 
294     EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice));
295 }
296 
TEST_F(SoundDoseManagerTest,GetDefaultForceComputeCsdOnAllDevices)297 TEST_F(SoundDoseManagerTest, GetDefaultForceComputeCsdOnAllDevices) {
298     EXPECT_FALSE(mSoundDoseManager->isComputeCsdForcedOnAllDevices());
299 }
300 
TEST_F(SoundDoseManagerTest,GetDefaultForceUseFrameworkMel)301 TEST_F(SoundDoseManagerTest, GetDefaultForceUseFrameworkMel) {
302     EXPECT_FALSE(mSoundDoseManager->isFrameworkMelForced());
303 }
304 
TEST_F(SoundDoseManagerTest,SetAudioDeviceCategoryStopsNonHeadphone)305 TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStopsNonHeadphone) {
306     media::ISoundDose::AudioDeviceCategory device1;
307     device1.address = "dev1";
308     device1.csdCompatible = false;
309     device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
310     const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
311 
312     // this will mark the device as active
313     mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
314     EXPECT_CALL(*mMelReporterCallback.get(), stopMelComputationForDeviceId).Times(1);
315 
316     mSoundDoseManager->setAudioDeviceCategory(device1);
317 }
318 
TEST_F(SoundDoseManagerTest,SetAudioDeviceCategoryStartsHeadphone)319 TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStartsHeadphone) {
320     media::ISoundDose::AudioDeviceCategory device1;
321     device1.address = "dev1";
322     device1.csdCompatible = true;
323     device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
324     const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
325 
326         // this will mark the device as active
327     mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
328     EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1);
329 
330     mSoundDoseManager->setAudioDeviceCategory(device1);
331 }
332 
TEST_F(SoundDoseManagerTest,InitCachedAudioDevicesStartsOnlyActiveDevices)333 TEST_F(SoundDoseManagerTest, InitCachedAudioDevicesStartsOnlyActiveDevices) {
334     media::ISoundDose::AudioDeviceCategory device1;
335     media::ISoundDose::AudioDeviceCategory device2;
336     device1.address = "dev1";
337     device1.csdCompatible = true;
338     device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
339     device2.address = "dev2";
340     device2.csdCompatible = true;
341     device2.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
342     const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
343     std::vector<media::ISoundDose::AudioDeviceCategory> btDevices = {device1, device2};
344 
345     // this will mark the device as active
346     mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
347     EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1);
348 
349     mSoundDoseManager->initCachedAudioDeviceCategories(btDevices);
350 }
351 
352 
353 }  // namespace
354 }  // namespace android
355