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 <gmock/gmock.h>
24 #include <gtest/gtest.h>
25 #include <media/AidlConversionCppNdk.h>
26
27 namespace android {
28 namespace {
29
30 using aidl::android::hardware::audio::core::sounddose::BnSoundDose;
31 using aidl::android::media::audio::common::AudioDevice;
32 using aidl::android::media::audio::common::AudioDeviceAddress;
33
34 class HalSoundDoseMock : public BnSoundDose {
35 public:
36 MOCK_METHOD(ndk::ScopedAStatus, getOutputRs2UpperBound, (float*), (override));
37 MOCK_METHOD(ndk::ScopedAStatus, setOutputRs2UpperBound, (float), (override));
38 MOCK_METHOD(ndk::ScopedAStatus, registerSoundDoseCallback,
39 (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>&), (override));
40 };
41
42 class MelReporterCallback : public IMelReporterCallback {
43 public:
44 MOCK_METHOD(void, startMelComputationForDeviceId, (audio_port_handle_t), (override));
45 MOCK_METHOD(void, stopMelComputationForDeviceId, (audio_port_handle_t), (override));
46 };
47
48 constexpr char kPrimaryModule[] = "primary";
49 constexpr char kSecondaryModule[] = "secondary";
50
51 class SoundDoseManagerTest : public ::testing::Test {
52 protected:
SetUp()53 void SetUp() override {
54 mMelReporterCallback = sp<MelReporterCallback>::make();
55 mSoundDoseManager = sp<SoundDoseManager>::make(mMelReporterCallback);
56 mHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
57 mSecondaryHalSoundDose = ndk::SharedRefBase::make<HalSoundDoseMock>();
58
59 ON_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound)
60 .WillByDefault([] (float rs2) {
61 EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2);
62 return ndk::ScopedAStatus::ok();
63 });
64 ON_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound)
65 .WillByDefault([] (float rs2) {
66 EXPECT_EQ(rs2, ISoundDose::DEFAULT_MAX_RS2);
67 return ndk::ScopedAStatus::ok();
68 });
69 }
70
71 sp<MelReporterCallback> mMelReporterCallback;
72 sp<SoundDoseManager> mSoundDoseManager;
73 std::shared_ptr<HalSoundDoseMock> mHalSoundDose;
74 std::shared_ptr<HalSoundDoseMock> mSecondaryHalSoundDose;
75 };
76
TEST_F(SoundDoseManagerTest,GetProcessorForExistingStream)77 TEST_F(SoundDoseManagerTest, GetProcessorForExistingStream) {
78 sp<audio_utils::MelProcessor> processor1 =
79 mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
80 /*streamHandle=*/1,
81 /*sampleRate*/44100,
82 /*channelCount*/2,
83 /*format*/AUDIO_FORMAT_PCM_FLOAT);
84 sp<audio_utils::MelProcessor> processor2 =
85 mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
86 /*streamHandle=*/1,
87 /*sampleRate*/44100,
88 /*channelCount*/2,
89 /*format*/AUDIO_FORMAT_PCM_FLOAT);
90
91 EXPECT_EQ(processor1, processor2);
92 }
93
TEST_F(SoundDoseManagerTest,RemoveExistingStream)94 TEST_F(SoundDoseManagerTest, RemoveExistingStream) {
95 sp<audio_utils::MelProcessor> processor1 =
96 mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/1,
97 /*streamHandle=*/1,
98 /*sampleRate*/44100,
99 /*channelCount*/2,
100 /*format*/AUDIO_FORMAT_PCM_FLOAT);
101
102 mSoundDoseManager->removeStreamProcessor(1);
103 sp<audio_utils::MelProcessor> processor2 =
104 mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
105 /*streamHandle=*/1,
106 /*sampleRate*/44100,
107 /*channelCount*/2,
108 /*format*/AUDIO_FORMAT_PCM_FLOAT);
109
110 EXPECT_NE(processor1, processor2);
111 }
112
TEST_F(SoundDoseManagerTest,NewMelValuesCacheNewRecord)113 TEST_F(SoundDoseManagerTest, NewMelValuesCacheNewRecord) {
114 std::vector<float>mels{1, 1};
115
116 mSoundDoseManager->onNewMelValues(mels, 0, mels.size(), /*deviceId=*/1);
117
118 EXPECT_EQ(mSoundDoseManager->getCachedMelRecordsSize(), size_t{1});
119 }
120
TEST_F(SoundDoseManagerTest,InvalidHalInterfaceIsNotSet)121 TEST_F(SoundDoseManagerTest, InvalidHalInterfaceIsNotSet) {
122 EXPECT_FALSE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, nullptr));
123 }
124
TEST_F(SoundDoseManagerTest,SetHalSoundDoseDisablesNewMelProcessorCallbacks)125 TEST_F(SoundDoseManagerTest, SetHalSoundDoseDisablesNewMelProcessorCallbacks) {
126 EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
127 EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
128 .Times(1)
129 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
130 EXPECT_NE(nullptr, callback);
131 return ndk::ScopedAStatus::ok();
132 });
133
134 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
135
136 EXPECT_EQ(nullptr, mSoundDoseManager->getOrCreateProcessorForDevice(/*deviceId=*/2,
137 /*streamHandle=*/1,
138 /*sampleRate*/44100,
139 /*channelCount*/2,
140 /*format*/AUDIO_FORMAT_PCM_FLOAT));
141 }
142
TEST_F(SoundDoseManagerTest,SetHalSoundDoseRegistersHalCallbacks)143 TEST_F(SoundDoseManagerTest, SetHalSoundDoseRegistersHalCallbacks) {
144 EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
145 EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
146 .Times(1)
147 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
148 EXPECT_NE(nullptr, callback);
149 return ndk::ScopedAStatus::ok();
150 });
151 EXPECT_CALL(*mSecondaryHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
152 EXPECT_CALL(*mSecondaryHalSoundDose.get(), registerSoundDoseCallback)
153 .Times(1)
154 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
155 EXPECT_NE(nullptr, callback);
156 return ndk::ScopedAStatus::ok();
157 });
158
159 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
160 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kSecondaryModule,
161 mSecondaryHalSoundDose));
162 }
163
TEST_F(SoundDoseManagerTest,MomentaryExposureFromHalWithNoAddressIllegalArgument)164 TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalWithNoAddressIllegalArgument) {
165 std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
166
167 EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
168 EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
169 .Times(1)
170 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
171 halCallback = callback;
172 return ndk::ScopedAStatus::ok();
173 });
174
175 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
176
177 EXPECT_NE(nullptr, halCallback);
178 AudioDevice audioDevice = {};
179 audioDevice.address.set<AudioDeviceAddress::id>("test");
180 auto status = halCallback->onMomentaryExposureWarning(
181 /*in_currentDbA=*/101.f, audioDevice);
182 EXPECT_FALSE(status.isOk());
183 }
184
TEST_F(SoundDoseManagerTest,MomentaryExposureFromHalAfterInternalSelectedReturnsException)185 TEST_F(SoundDoseManagerTest, MomentaryExposureFromHalAfterInternalSelectedReturnsException) {
186 std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
187
188 EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
189 EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
190 .Times(1)
191 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
192 halCallback = callback;
193 return ndk::ScopedAStatus::ok();
194 });
195
196 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
197 EXPECT_NE(nullptr, halCallback);
198 mSoundDoseManager->resetHalSoundDoseInterfaces();
199
200 AudioDevice audioDevice = {};
201 audioDevice.address.set<AudioDeviceAddress::id>("test");
202 auto status = halCallback->onMomentaryExposureWarning(
203 /*in_currentDbA=*/101.f, audioDevice);
204 EXPECT_FALSE(status.isOk());
205 }
206
TEST_F(SoundDoseManagerTest,OnNewMelValuesFromHalWithNoAddressIllegalArgument)207 TEST_F(SoundDoseManagerTest, OnNewMelValuesFromHalWithNoAddressIllegalArgument) {
208 std::shared_ptr<ISoundDose::IHalSoundDoseCallback> halCallback;
209
210 EXPECT_CALL(*mHalSoundDose.get(), setOutputRs2UpperBound).Times(1);
211 EXPECT_CALL(*mHalSoundDose.get(), registerSoundDoseCallback)
212 .Times(1)
213 .WillOnce([&] (const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& callback) {
214 halCallback = callback;
215 return ndk::ScopedAStatus::ok();
216 });
217
218 EXPECT_TRUE(mSoundDoseManager->setHalSoundDoseInterface(kPrimaryModule, mHalSoundDose));
219
220 EXPECT_NE(nullptr, halCallback);
221 AudioDevice audioDevice = {};
222 audioDevice.address.set<AudioDeviceAddress::id>("test");
223 auto status = halCallback->onNewMelValues(/*in_melRecord=*/{}, audioDevice);
224 EXPECT_FALSE(status.isOk());
225 }
226
TEST_F(SoundDoseManagerTest,GetIdReturnsMappedAddress)227 TEST_F(SoundDoseManagerTest, GetIdReturnsMappedAddress) {
228 const std::string address = "testAddress";
229 const audio_port_handle_t deviceId = 2;
230 const audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET;
231 const AudioDeviceTypeAddr adt{deviceType, address};
232 auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice(
233 deviceType, address.c_str());
234 ASSERT_TRUE(audioDevice.ok());
235
236 mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
237
238 EXPECT_EQ(deviceId, mSoundDoseManager->getIdForAudioDevice(audioDevice.value()));
239 }
240
TEST_F(SoundDoseManagerTest,GetAfterClearIdReturnsNone)241 TEST_F(SoundDoseManagerTest, GetAfterClearIdReturnsNone) {
242 const std::string address = "testAddress";
243 const audio_devices_t deviceType = AUDIO_DEVICE_OUT_WIRED_HEADSET;
244 const AudioDeviceTypeAddr adt{deviceType, address};
245 const audio_port_handle_t deviceId = 2;
246 auto audioDevice = aidl::android::legacy2aidl_audio_device_AudioDevice(
247 deviceType, address.c_str());
248 ASSERT_TRUE(audioDevice.ok());
249
250 mSoundDoseManager->mapAddressToDeviceId(adt, deviceId);
251 mSoundDoseManager->clearMapDeviceIdEntries(deviceId);
252
253 EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice.value()));
254 }
255
TEST_F(SoundDoseManagerTest,GetUnmappedIdReturnsHandleNone)256 TEST_F(SoundDoseManagerTest, GetUnmappedIdReturnsHandleNone) {
257 const std::string address = "testAddress";
258 AudioDevice audioDevice;
259 audioDevice.address.set<AudioDeviceAddress::id>(address);
260
261 EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, mSoundDoseManager->getIdForAudioDevice(audioDevice));
262 }
263
TEST_F(SoundDoseManagerTest,GetDefaultForceComputeCsdOnAllDevices)264 TEST_F(SoundDoseManagerTest, GetDefaultForceComputeCsdOnAllDevices) {
265 EXPECT_FALSE(mSoundDoseManager->forceComputeCsdOnAllDevices());
266 }
267
TEST_F(SoundDoseManagerTest,GetDefaultForceUseFrameworkMel)268 TEST_F(SoundDoseManagerTest, GetDefaultForceUseFrameworkMel) {
269 EXPECT_FALSE(mSoundDoseManager->forceUseFrameworkMel());
270 }
271
TEST_F(SoundDoseManagerTest,SetAudioDeviceCategoryStopsNonHeadphone)272 TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStopsNonHeadphone) {
273 media::ISoundDose::AudioDeviceCategory device1;
274 device1.address = "dev1";
275 device1.csdCompatible = false;
276 device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
277 const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
278
279 // this will mark the device as active
280 mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
281 EXPECT_CALL(*mMelReporterCallback.get(), stopMelComputationForDeviceId).Times(1);
282
283 mSoundDoseManager->setAudioDeviceCategory(device1);
284 }
285
TEST_F(SoundDoseManagerTest,SetAudioDeviceCategoryStartsHeadphone)286 TEST_F(SoundDoseManagerTest, SetAudioDeviceCategoryStartsHeadphone) {
287 media::ISoundDose::AudioDeviceCategory device1;
288 device1.address = "dev1";
289 device1.csdCompatible = true;
290 device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
291 const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
292
293 // this will mark the device as active
294 mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
295 EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1);
296
297 mSoundDoseManager->setAudioDeviceCategory(device1);
298 }
299
TEST_F(SoundDoseManagerTest,InitCachedAudioDevicesStartsOnlyActiveDevices)300 TEST_F(SoundDoseManagerTest, InitCachedAudioDevicesStartsOnlyActiveDevices) {
301 media::ISoundDose::AudioDeviceCategory device1;
302 media::ISoundDose::AudioDeviceCategory device2;
303 device1.address = "dev1";
304 device1.csdCompatible = true;
305 device1.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
306 device2.address = "dev2";
307 device2.csdCompatible = true;
308 device2.internalAudioType = AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
309 const AudioDeviceTypeAddr dev1Adt{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, device1.address};
310 std::vector<media::ISoundDose::AudioDeviceCategory> btDevices = {device1, device2};
311
312 // this will mark the device as active
313 mSoundDoseManager->mapAddressToDeviceId(dev1Adt, /*deviceId=*/1);
314 EXPECT_CALL(*mMelReporterCallback.get(), startMelComputationForDeviceId).Times(1);
315
316 mSoundDoseManager->initCachedAudioDeviceCategories(btDevices);
317 }
318
319
320 } // namespace
321 } // namespace android
322