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