1 /*
2 **
3 ** Copyright 2022, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 // #define LOG_NDEBUG 0
19 #define LOG_TAG "SoundDoseManager"
20
21 #include "SoundDoseManager.h"
22
23 #include "android/media/SoundDoseRecord.h"
24 #include <algorithm>
25 #include <android-base/stringprintf.h>
26 #include <cinttypes>
27 #include <ctime>
28 #include <functional>
29 #include <media/AidlConversionCppNdk.h>
30 #include <utils/Log.h>
31
32 namespace android {
33
34 using aidl::android::media::audio::common::AudioDevice;
35
36 namespace {
37
38 // Port handle used when CSD is computed on all devices. Should be a different value than
39 // AUDIO_PORT_HANDLE_NONE which is associated with a sound dose callback failure
40 constexpr audio_port_handle_t CSD_ON_ALL_DEVICES_PORT_HANDLE = -1;
41
getMonotonicSecond()42 int64_t getMonotonicSecond() {
43 struct timespec now_ts;
44 if (clock_gettime(CLOCK_MONOTONIC, &now_ts) != 0) {
45 ALOGE("%s: cannot get timestamp", __func__);
46 return -1;
47 }
48 return now_ts.tv_sec;
49 }
50
51 constexpr float kDefaultRs2LowerBound = 80.f; // dBA
52
53 } // namespace
54
getOrCreateProcessorForDevice(audio_port_handle_t deviceId,audio_io_handle_t streamHandle,uint32_t sampleRate,size_t channelCount,audio_format_t format)55 sp<audio_utils::MelProcessor> SoundDoseManager::getOrCreateProcessorForDevice(
56 audio_port_handle_t deviceId, audio_io_handle_t streamHandle, uint32_t sampleRate,
57 size_t channelCount, audio_format_t format) {
58 const std::lock_guard _l(mLock);
59
60 if (!mUseFrameworkMel && mHalSoundDose.size() > 0 && mEnabledCsd) {
61 ALOGD("%s: using HAL MEL computation, no MelProcessor needed.", __func__);
62 return nullptr;
63 }
64
65 auto streamProcessor = mActiveProcessors.find(streamHandle);
66 if (streamProcessor != mActiveProcessors.end()) {
67 auto processor = streamProcessor->second.promote();
68 // if processor is nullptr it means it was removed by the playback
69 // thread and can be replaced in the mActiveProcessors map
70 if (processor != nullptr) {
71 ALOGV("%s: found callback for stream id %d", __func__, streamHandle);
72 const auto activeTypeIt = mActiveDeviceTypes.find(deviceId);
73 if (activeTypeIt != mActiveDeviceTypes.end()) {
74 processor->setAttenuation(mMelAttenuationDB[activeTypeIt->second]);
75 }
76 processor->setDeviceId(deviceId);
77 processor->setOutputRs2UpperBound(mRs2UpperBound);
78 return processor;
79 }
80 }
81
82 ALOGV("%s: creating new callback for stream id %d", __func__, streamHandle);
83 sp<audio_utils::MelProcessor> melProcessor = sp<audio_utils::MelProcessor>::make(
84 sampleRate, channelCount, format, this, deviceId, mRs2UpperBound);
85 const auto activeTypeIt = mActiveDeviceTypes.find(deviceId);
86 if (activeTypeIt != mActiveDeviceTypes.end()) {
87 melProcessor->setAttenuation(mMelAttenuationDB[activeTypeIt->second]);
88 }
89 mActiveProcessors[streamHandle] = melProcessor;
90 return melProcessor;
91 }
92
setHalSoundDoseInterface(const std::string & module,const std::shared_ptr<ISoundDose> & halSoundDose)93 bool SoundDoseManager::setHalSoundDoseInterface(const std::string &module,
94 const std::shared_ptr<ISoundDose> &halSoundDose) {
95 ALOGV("%s", __func__);
96
97 if (halSoundDose == nullptr) {
98 ALOGI("%s: passed ISoundDose object is null", __func__);
99 return false;
100 }
101
102 std::shared_ptr<HalSoundDoseCallback> halSoundDoseCallback;
103 {
104 const std::lock_guard _l(mLock);
105
106 if (mHalSoundDose.find(module) != mHalSoundDose.end()) {
107 ALOGW("%s: Module %s already has a sound dose HAL assigned, skipping", __func__,
108 module.c_str());
109 return false;
110 }
111 mHalSoundDose[module] = halSoundDose;
112
113 if (!halSoundDose->setOutputRs2UpperBound(mRs2UpperBound).isOk()) {
114 ALOGW("%s: Cannot set RS2 value for momentary exposure %f",
115 __func__,
116 mRs2UpperBound);
117 }
118
119 // initialize the HAL sound dose callback lazily
120 if (mHalSoundDoseCallback == nullptr) {
121 mHalSoundDoseCallback =
122 ndk::SharedRefBase::make<HalSoundDoseCallback>(this);
123 }
124 halSoundDoseCallback = mHalSoundDoseCallback;
125 }
126
127 auto status = halSoundDose->registerSoundDoseCallback(halSoundDoseCallback);
128
129 if (!status.isOk()) {
130 // Not a warning since this can happen if the callback was registered before
131 ALOGI("%s: Cannot register HAL sound dose callback with status message: %s",
132 __func__,
133 status.getMessage());
134 }
135
136 return true;
137 }
138
resetHalSoundDoseInterfaces()139 void SoundDoseManager::resetHalSoundDoseInterfaces() {
140 ALOGV("%s", __func__);
141
142 const std::lock_guard _l(mLock);
143 mHalSoundDose.clear();
144 }
145
setOutputRs2UpperBound(float rs2Value)146 void SoundDoseManager::setOutputRs2UpperBound(float rs2Value) {
147 ALOGV("%s", __func__);
148 const std::lock_guard _l(mLock);
149
150 if (!mUseFrameworkMel && mHalSoundDose.size() > 0) {
151 bool success = true;
152 for (auto& halSoundDose : mHalSoundDose) {
153 // using the HAL sound dose interface
154 if (!halSoundDose.second->setOutputRs2UpperBound(rs2Value).isOk()) {
155 ALOGE("%s: Cannot set RS2 value for momentary exposure %f", __func__, rs2Value);
156 success = false;
157 break;
158 }
159 }
160
161 if (success) {
162 mRs2UpperBound = rs2Value;
163 } else {
164 // restore all RS2 upper bounds to the previous value
165 for (auto& halSoundDose : mHalSoundDose) {
166 halSoundDose.second->setOutputRs2UpperBound(mRs2UpperBound);
167 }
168 }
169 return;
170 }
171
172 for (auto& streamProcessor : mActiveProcessors) {
173 const sp<audio_utils::MelProcessor> processor = streamProcessor.second.promote();
174 if (processor != nullptr) {
175 const status_t result = processor->setOutputRs2UpperBound(rs2Value);
176 if (result != NO_ERROR) {
177 ALOGW("%s: could not set RS2 upper bound %f for stream %d", __func__, rs2Value,
178 streamProcessor.first);
179 return;
180 }
181 mRs2UpperBound = rs2Value;
182 }
183 }
184 }
185
removeStreamProcessor(audio_io_handle_t streamHandle)186 void SoundDoseManager::removeStreamProcessor(audio_io_handle_t streamHandle) {
187 const std::lock_guard _l(mLock);
188 auto callbackToRemove = mActiveProcessors.find(streamHandle);
189 if (callbackToRemove != mActiveProcessors.end()) {
190 mActiveProcessors.erase(callbackToRemove);
191 }
192 }
193
getAttenuationForDeviceId(audio_port_handle_t id) const194 float SoundDoseManager::getAttenuationForDeviceId(audio_port_handle_t id) const {
195 float attenuation = 0.f;
196
197 const std::lock_guard _l(mLock);
198 const auto deviceTypeIt = mActiveDeviceTypes.find(id);
199 if (deviceTypeIt != mActiveDeviceTypes.end()) {
200 auto attenuationIt = mMelAttenuationDB.find(deviceTypeIt->second);
201 if (attenuationIt != mMelAttenuationDB.end()) {
202 attenuation = attenuationIt->second;
203 }
204 }
205
206 return attenuation;
207 }
208
getIdForAudioDevice(const AudioDevice & audioDevice) const209 audio_port_handle_t SoundDoseManager::getIdForAudioDevice(const AudioDevice& audioDevice) const {
210 if (isComputeCsdForcedOnAllDevices()) {
211 // If CSD is forced on all devices return random port id. Used only in testing.
212 // This is necessary since the patches that are registered before
213 // setComputeCsdOnAllDevices will not be contributing to mActiveDevices
214 return CSD_ON_ALL_DEVICES_PORT_HANDLE;
215 }
216
217 const std::lock_guard _l(mLock);
218
219 audio_devices_t type;
220 std::string address;
221 auto result = aidl::android::aidl2legacy_AudioDevice_audio_device(
222 audioDevice, &type, &address);
223 if (result != NO_ERROR) {
224 ALOGE("%s: could not convert from AudioDevice to AudioDeviceTypeAddr", __func__);
225 return AUDIO_PORT_HANDLE_NONE;
226 }
227
228 auto adt = AudioDeviceTypeAddr(type, address);
229 auto deviceIt = mActiveDevices.find(adt);
230 if (deviceIt == mActiveDevices.end()) {
231 ALOGI("%s: could not find port id for device %s", __func__, adt.toString().c_str());
232 return AUDIO_PORT_HANDLE_NONE;
233 }
234
235 if (audio_is_ble_out_device(type) || audio_is_a2dp_device(type)) {
236 const auto btDeviceIt = mBluetoothDevicesWithCsd.find(std::make_pair(address, type));
237 if (btDeviceIt == mBluetoothDevicesWithCsd.end() || !btDeviceIt->second) {
238 ALOGI("%s: bt device %s does not support sound dose", __func__,
239 adt.toString().c_str());
240 return AUDIO_PORT_HANDLE_NONE;
241 }
242 }
243 return deviceIt->second;
244 }
245
mapAddressToDeviceId(const AudioDeviceTypeAddr & adt,const audio_port_handle_t deviceId)246 void SoundDoseManager::mapAddressToDeviceId(const AudioDeviceTypeAddr& adt,
247 const audio_port_handle_t deviceId) {
248 const std::lock_guard _l(mLock);
249 ALOGI("%s: map address: %d to device id: %d", __func__, adt.mType, deviceId);
250 mActiveDevices[adt] = deviceId;
251 mActiveDeviceTypes[deviceId] = adt.mType;
252 }
253
clearMapDeviceIdEntries(audio_port_handle_t deviceId)254 void SoundDoseManager::clearMapDeviceIdEntries(audio_port_handle_t deviceId) {
255 const std::lock_guard _l(mLock);
256 for (auto activeDevice = mActiveDevices.begin(); activeDevice != mActiveDevices.end();) {
257 if (activeDevice->second == deviceId) {
258 ALOGI("%s: clear mapping type: %d to deviceId: %d",
259 __func__, activeDevice->first.mType, deviceId);
260 activeDevice = mActiveDevices.erase(activeDevice);
261 continue;
262 }
263 ++activeDevice;
264 }
265 mActiveDeviceTypes.erase(deviceId);
266 }
267
onMomentaryExposureWarning(float in_currentDbA,const AudioDevice & in_audioDevice)268 ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onMomentaryExposureWarning(
269 float in_currentDbA, const AudioDevice& in_audioDevice) {
270 sp<SoundDoseManager> soundDoseManager;
271 {
272 const std::lock_guard _l(mCbLock);
273 soundDoseManager = mSoundDoseManager.promote();
274 if (soundDoseManager == nullptr) {
275 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
276 }
277 }
278
279 if (!soundDoseManager->useHalSoundDose()) {
280 ALOGW("%s: HAL sound dose interface deactivated. Ignoring", __func__);
281 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
282 }
283
284 auto id = soundDoseManager->getIdForAudioDevice(in_audioDevice);
285 if (id == AUDIO_PORT_HANDLE_NONE) {
286 ALOGI("%s: no mapped id for audio device with type %d and address %s",
287 __func__, static_cast<int>(in_audioDevice.type.type),
288 in_audioDevice.address.toString().c_str());
289 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
290 }
291
292 float attenuation = soundDoseManager->getAttenuationForDeviceId(id);
293 ALOGV("%s: attenuating received momentary exposure with %f dB", __func__, attenuation);
294 // TODO: remove attenuation when enforcing HAL MELs to always be attenuated
295 soundDoseManager->onMomentaryExposure(in_currentDbA - attenuation, id);
296
297 return ndk::ScopedAStatus::ok();
298 }
299
onNewMelValues(const ISoundDose::IHalSoundDoseCallback::MelRecord & in_melRecord,const AudioDevice & in_audioDevice)300 ndk::ScopedAStatus SoundDoseManager::HalSoundDoseCallback::onNewMelValues(
301 const ISoundDose::IHalSoundDoseCallback::MelRecord& in_melRecord,
302 const AudioDevice& in_audioDevice) {
303 sp<SoundDoseManager> soundDoseManager;
304 {
305 const std::lock_guard _l(mCbLock);
306 soundDoseManager = mSoundDoseManager.promote();
307 if (soundDoseManager == nullptr) {
308 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
309 }
310 }
311
312 if (!soundDoseManager->useHalSoundDose()) {
313 ALOGW("%s: HAL sound dose interface deactivated. Ignoring", __func__);
314 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
315 }
316
317 auto id = soundDoseManager->getIdForAudioDevice(in_audioDevice);
318 if (id == AUDIO_PORT_HANDLE_NONE) {
319 ALOGI("%s: no mapped id for audio device with type %d and address %s",
320 __func__, static_cast<int>(in_audioDevice.type.type),
321 in_audioDevice.address.toString().c_str());
322 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
323 }
324
325 // TODO: introduce timestamp in onNewMelValues callback
326 soundDoseManager->onNewMelValues(in_melRecord.melValues, 0, in_melRecord.melValues.size(),
327 id, /*attenuated=*/false);
328
329 return ndk::ScopedAStatus::ok();
330 }
331
binderDied(__unused const wp<IBinder> & who)332 void SoundDoseManager::SoundDose::binderDied(__unused const wp<IBinder>& who) {
333 ALOGV("%s", __func__);
334
335 auto soundDoseManager = mSoundDoseManager.promote();
336 if (soundDoseManager != nullptr) {
337 soundDoseManager->resetSoundDose();
338 }
339 }
340
setOutputRs2UpperBound(float value)341 binder::Status SoundDoseManager::SoundDose::setOutputRs2UpperBound(float value) {
342 ALOGV("%s", __func__);
343 auto soundDoseManager = mSoundDoseManager.promote();
344 if (soundDoseManager != nullptr) {
345 soundDoseManager->setOutputRs2UpperBound(value);
346 }
347 return binder::Status::ok();
348 }
349
resetCsd(float currentCsd,const std::vector<media::SoundDoseRecord> & records)350 binder::Status SoundDoseManager::SoundDose::resetCsd(
351 float currentCsd, const std::vector<media::SoundDoseRecord>& records) {
352 ALOGV("%s", __func__);
353 auto soundDoseManager = mSoundDoseManager.promote();
354 if (soundDoseManager != nullptr) {
355 soundDoseManager->resetCsd(currentCsd, records);
356 }
357 return binder::Status::ok();
358 }
359
updateAttenuation(float attenuationDB,int device)360 binder::Status SoundDoseManager::SoundDose::updateAttenuation(float attenuationDB, int device) {
361 ALOGV("%s", __func__);
362 auto soundDoseManager = mSoundDoseManager.promote();
363 if (soundDoseManager != nullptr) {
364 soundDoseManager->updateAttenuation(attenuationDB, static_cast<audio_devices_t>(device));
365 }
366 return binder::Status::ok();
367 }
368
setCsdEnabled(bool enabled)369 binder::Status SoundDoseManager::SoundDose::setCsdEnabled(bool enabled) {
370 ALOGV("%s", __func__);
371 auto soundDoseManager = mSoundDoseManager.promote();
372 if (soundDoseManager != nullptr) {
373 soundDoseManager->setCsdEnabled(enabled);
374 }
375 return binder::Status::ok();
376 }
377
initCachedAudioDeviceCategories(const std::vector<media::ISoundDose::AudioDeviceCategory> & btDeviceCategories)378 binder::Status SoundDoseManager::SoundDose::initCachedAudioDeviceCategories(
379 const std::vector<media::ISoundDose::AudioDeviceCategory>& btDeviceCategories) {
380 ALOGV("%s", __func__);
381 auto soundDoseManager = mSoundDoseManager.promote();
382 if (soundDoseManager != nullptr) {
383 soundDoseManager->initCachedAudioDeviceCategories(btDeviceCategories);
384 }
385 return binder::Status::ok();
386 }
setAudioDeviceCategory(const media::ISoundDose::AudioDeviceCategory & btAudioDevice)387 binder::Status SoundDoseManager::SoundDose::setAudioDeviceCategory(
388 const media::ISoundDose::AudioDeviceCategory& btAudioDevice) {
389 ALOGV("%s", __func__);
390 auto soundDoseManager = mSoundDoseManager.promote();
391 if (soundDoseManager != nullptr) {
392 soundDoseManager->setAudioDeviceCategory(btAudioDevice);
393 }
394 return binder::Status::ok();
395 }
396
getOutputRs2UpperBound(float * value)397 binder::Status SoundDoseManager::SoundDose::getOutputRs2UpperBound(float* value) {
398 ALOGV("%s", __func__);
399 auto soundDoseManager = mSoundDoseManager.promote();
400 if (soundDoseManager != nullptr) {
401 const std::lock_guard _l(soundDoseManager->mLock);
402 *value = soundDoseManager->mRs2UpperBound;
403 }
404 return binder::Status::ok();
405 }
406
getCsd(float * value)407 binder::Status SoundDoseManager::SoundDose::getCsd(float* value) {
408 ALOGV("%s", __func__);
409 auto soundDoseManager = mSoundDoseManager.promote();
410 if (soundDoseManager != nullptr) {
411 *value = soundDoseManager->mMelAggregator->getCsd();
412 }
413 return binder::Status::ok();
414 }
415
forceUseFrameworkMel(bool useFrameworkMel)416 binder::Status SoundDoseManager::SoundDose::forceUseFrameworkMel(bool useFrameworkMel) {
417 ALOGV("%s", __func__);
418 auto soundDoseManager = mSoundDoseManager.promote();
419 if (soundDoseManager != nullptr) {
420 soundDoseManager->setUseFrameworkMel(useFrameworkMel);
421 }
422 return binder::Status::ok();
423 }
424
forceComputeCsdOnAllDevices(bool computeCsdOnAllDevices)425 binder::Status SoundDoseManager::SoundDose::forceComputeCsdOnAllDevices(
426 bool computeCsdOnAllDevices) {
427 ALOGV("%s", __func__);
428 auto soundDoseManager = mSoundDoseManager.promote();
429 if (soundDoseManager != nullptr) {
430 soundDoseManager->setComputeCsdOnAllDevices(computeCsdOnAllDevices);
431 }
432 return binder::Status::ok();
433 }
434
isSoundDoseHalSupported(bool * value)435 binder::Status SoundDoseManager::SoundDose::isSoundDoseHalSupported(bool* value) {
436 ALOGV("%s", __func__);
437 *value = false;
438 auto soundDoseManager = mSoundDoseManager.promote();
439 if (soundDoseManager != nullptr) {
440 *value = soundDoseManager->isSoundDoseHalSupported();
441 }
442 return binder::Status::ok();
443 }
444
updateAttenuation(float attenuationDB,audio_devices_t deviceType)445 void SoundDoseManager::updateAttenuation(float attenuationDB, audio_devices_t deviceType) {
446 const std::lock_guard _l(mLock);
447 ALOGV("%s: updating MEL processor attenuation for device type %d to %f",
448 __func__, deviceType, attenuationDB);
449 mMelAttenuationDB[deviceType] = attenuationDB;
450 for (const auto& mp : mActiveProcessors) {
451 auto melProcessor = mp.second.promote();
452 if (melProcessor != nullptr) {
453 auto deviceId = melProcessor->getDeviceId();
454 const auto deviceTypeIt = mActiveDeviceTypes.find(deviceId);
455 if (deviceTypeIt != mActiveDeviceTypes.end() &&
456 deviceTypeIt->second == deviceType) {
457 ALOGV("%s: set attenuation for deviceId %d to %f",
458 __func__, deviceId, attenuationDB);
459 melProcessor->setAttenuation(attenuationDB);
460 }
461 }
462 }
463 }
464
setCsdEnabled(bool enabled)465 void SoundDoseManager::setCsdEnabled(bool enabled) {
466 ALOGV("%s", __func__);
467
468 const std::lock_guard _l(mLock);
469 mEnabledCsd = enabled;
470
471 for (auto& activeEntry : mActiveProcessors) {
472 auto melProcessor = activeEntry.second.promote();
473 if (melProcessor != nullptr) {
474 if (enabled) {
475 melProcessor->resume();
476 } else {
477 melProcessor->pause();
478 }
479 }
480 }
481 }
482
isCsdEnabled()483 bool SoundDoseManager::isCsdEnabled() {
484 const std::lock_guard _l(mLock);
485 return mEnabledCsd;
486 }
487
initCachedAudioDeviceCategories(const std::vector<media::ISoundDose::AudioDeviceCategory> & deviceCategories)488 void SoundDoseManager::initCachedAudioDeviceCategories(
489 const std::vector<media::ISoundDose::AudioDeviceCategory>& deviceCategories) {
490 ALOGV("%s", __func__);
491 {
492 const std::lock_guard _l(mLock);
493 mBluetoothDevicesWithCsd.clear();
494 }
495 for (const auto& btDeviceCategory : deviceCategories) {
496 setAudioDeviceCategory(btDeviceCategory);
497 }
498 }
499
setAudioDeviceCategory(const media::ISoundDose::AudioDeviceCategory & audioDevice)500 void SoundDoseManager::setAudioDeviceCategory(
501 const media::ISoundDose::AudioDeviceCategory& audioDevice) {
502 ALOGV("%s: set BT audio device type with address %s to headphone %d", __func__,
503 audioDevice.address.c_str(), audioDevice.csdCompatible);
504
505 std::vector<audio_port_handle_t> devicesToStart;
506 std::vector<audio_port_handle_t> devicesToStop;
507 {
508 const std::lock_guard _l(mLock);
509 const auto deviceIt = mBluetoothDevicesWithCsd.find(
510 std::make_pair(audioDevice.address,
511 static_cast<audio_devices_t>(audioDevice.internalAudioType)));
512 if (deviceIt != mBluetoothDevicesWithCsd.end()) {
513 deviceIt->second = audioDevice.csdCompatible;
514 } else {
515 mBluetoothDevicesWithCsd.emplace(
516 std::make_pair(audioDevice.address,
517 static_cast<audio_devices_t>(audioDevice.internalAudioType)),
518 audioDevice.csdCompatible);
519 }
520
521 for (const auto &activeDevice: mActiveDevices) {
522 if (activeDevice.first.address() == audioDevice.address &&
523 activeDevice.first.mType ==
524 static_cast<audio_devices_t>(audioDevice.internalAudioType)) {
525 if (audioDevice.csdCompatible) {
526 devicesToStart.push_back(activeDevice.second);
527 } else {
528 devicesToStop.push_back(activeDevice.second);
529 }
530 }
531 }
532 }
533
534 for (const auto& deviceToStart : devicesToStart) {
535 mMelReporterCallback->startMelComputationForDeviceId(deviceToStart);
536 }
537 for (const auto& deviceToStop : devicesToStop) {
538 mMelReporterCallback->stopMelComputationForDeviceId(deviceToStop);
539 }
540 }
541
shouldComputeCsdForDeviceType(audio_devices_t device)542 bool SoundDoseManager::shouldComputeCsdForDeviceType(audio_devices_t device) {
543 if (!isCsdEnabled()) {
544 ALOGV("%s csd is disabled", __func__);
545 return false;
546 }
547 if (isComputeCsdForcedOnAllDevices()) {
548 return true;
549 }
550
551 switch (device) {
552 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
553 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
554 case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
555 case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
556 case AUDIO_DEVICE_OUT_USB_HEADSET:
557 case AUDIO_DEVICE_OUT_BLE_HEADSET:
558 case AUDIO_DEVICE_OUT_BLE_BROADCAST:
559 return true;
560 default:
561 return false;
562 }
563 }
564
shouldComputeCsdForDeviceWithAddress(const audio_devices_t type,const std::string & deviceAddress)565 bool SoundDoseManager::shouldComputeCsdForDeviceWithAddress(const audio_devices_t type,
566 const std::string& deviceAddress) {
567 if (!isCsdEnabled()) {
568 ALOGV("%s csd is disabled", __func__);
569 return false;
570 }
571 if (isComputeCsdForcedOnAllDevices()) {
572 return true;
573 }
574
575 if (!audio_is_ble_out_device(type) && !audio_is_a2dp_device(type)) {
576 return shouldComputeCsdForDeviceType(type);
577 }
578
579 const std::lock_guard _l(mLock);
580 const auto deviceIt = mBluetoothDevicesWithCsd.find(std::make_pair(deviceAddress, type));
581 return deviceIt != mBluetoothDevicesWithCsd.end() && deviceIt->second;
582 }
583
setUseFrameworkMel(bool useFrameworkMel)584 void SoundDoseManager::setUseFrameworkMel(bool useFrameworkMel) {
585 const std::lock_guard _l(mLock);
586 mUseFrameworkMel = useFrameworkMel;
587 }
588
isFrameworkMelForced() const589 bool SoundDoseManager::isFrameworkMelForced() const {
590 const std::lock_guard _l(mLock);
591 return mUseFrameworkMel;
592 }
593
setComputeCsdOnAllDevices(bool computeCsdOnAllDevices)594 void SoundDoseManager::setComputeCsdOnAllDevices(bool computeCsdOnAllDevices) {
595 bool changed = false;
596 {
597 const std::lock_guard _l(mLock);
598 if (mHalSoundDose.size() != 0) {
599 // when using the HAL path we cannot enforce to deliver values for all devices
600 changed = mUseFrameworkMel != computeCsdOnAllDevices;
601 mUseFrameworkMel = computeCsdOnAllDevices;
602 }
603 mComputeCsdOnAllDevices = computeCsdOnAllDevices;
604 }
605 if (changed && computeCsdOnAllDevices) {
606 mMelReporterCallback->applyAllAudioPatches();
607 }
608 }
609
isComputeCsdForcedOnAllDevices() const610 bool SoundDoseManager::isComputeCsdForcedOnAllDevices() const {
611 const std::lock_guard _l(mLock);
612 return mComputeCsdOnAllDevices;
613 }
614
isSoundDoseHalSupported() const615 bool SoundDoseManager::isSoundDoseHalSupported() const {
616 {
617 const std::lock_guard _l(mLock);
618 if (!mEnabledCsd) return false;
619 }
620
621 return useHalSoundDose();
622 }
623
useHalSoundDose() const624 bool SoundDoseManager::useHalSoundDose() const {
625 const std::lock_guard _l(mLock);
626 return !mUseFrameworkMel && mHalSoundDose.size() > 0;
627 }
628
resetSoundDose()629 void SoundDoseManager::resetSoundDose() {
630 const std::lock_guard lock(mLock);
631 mSoundDose = nullptr;
632 }
633
resetCsd(float currentCsd,const std::vector<media::SoundDoseRecord> & records)634 void SoundDoseManager::resetCsd(float currentCsd,
635 const std::vector<media::SoundDoseRecord>& records) {
636 const std::lock_guard lock(mLock);
637 std::vector<audio_utils::CsdRecord> resetRecords;
638 resetRecords.reserve(records.size());
639 for (const auto& record : records) {
640 resetRecords.emplace_back(record.timestamp, record.duration, record.value,
641 record.averageMel);
642 }
643
644 mMelAggregator->reset(currentCsd, resetRecords);
645 }
646
onNewMelValues(const std::vector<float> & mels,size_t offset,size_t length,audio_port_handle_t deviceId,bool attenuated) const647 void SoundDoseManager::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
648 audio_port_handle_t deviceId, bool attenuated) const {
649 ALOGV("%s", __func__);
650
651 sp<media::ISoundDoseCallback> soundDoseCallback;
652 std::vector<audio_utils::CsdRecord> records;
653 float currentCsd;
654
655 // TODO: delete this case when enforcing HAL MELs to always be attenuated
656 float attenuation = attenuated ? 0.0f : getAttenuationForDeviceId(deviceId);
657
658 {
659 const std::lock_guard _l(mLock);
660 if (!mEnabledCsd) {
661 return;
662 }
663
664 const int64_t timestampSec = getMonotonicSecond();
665
666 if (attenuated) {
667 records = mMelAggregator->aggregateAndAddNewMelRecord(audio_utils::MelRecord(
668 deviceId,
669 std::vector<float>(mels.begin() + offset, mels.begin() + offset + length),
670 timestampSec - length));
671 } else {
672 ALOGV("%s: attenuating received values with %f dB", __func__, attenuation);
673
674 // Extracting all intervals that contain values >= RS2 low limit (80dBA) after the
675 // attenuation is applied
676 size_t start = offset;
677 size_t stop = offset;
678 for (; stop < mels.size() && stop < offset + length; ++stop) {
679 if (mels[stop] - attenuation < kDefaultRs2LowerBound) {
680 if (start < stop) {
681 std::vector<float> attMel(stop-start, -attenuation);
682 // attMel[i] = mels[i] + attenuation, i in [start, stop)
683 std::transform(mels.begin() + start, mels.begin() + stop, attMel.begin(),
684 attMel.begin(), std::plus<float>());
685 std::vector<audio_utils::CsdRecord> newRec =
686 mMelAggregator->aggregateAndAddNewMelRecord(
687 audio_utils::MelRecord(deviceId,
688 attMel,
689 timestampSec - length + start -
690 offset));
691 std::copy(newRec.begin(), newRec.end(), std::back_inserter(records));
692 }
693 start = stop+1;
694 }
695 }
696 if (start < stop) {
697 std::vector<float> attMel(stop-start, -attenuation);
698 // attMel[i] = mels[i] + attenuation, i in [start, stop)
699 std::transform(mels.begin() + start, mels.begin() + stop, attMel.begin(),
700 attMel.begin(), std::plus<float>());
701 std::vector<audio_utils::CsdRecord> newRec =
702 mMelAggregator->aggregateAndAddNewMelRecord(
703 audio_utils::MelRecord(deviceId,
704 attMel,
705 timestampSec - length + start -
706 offset));
707 std::copy(newRec.begin(), newRec.end(), std::back_inserter(records));
708 }
709 }
710
711 currentCsd = mMelAggregator->getCsd();
712 }
713
714 soundDoseCallback = getSoundDoseCallback();
715
716 if (records.size() > 0 && soundDoseCallback != nullptr) {
717 std::vector<media::SoundDoseRecord> newRecordsToReport;
718 newRecordsToReport.resize(records.size());
719 for (const auto& record : records) {
720 newRecordsToReport.emplace_back(csdRecordToSoundDoseRecord(record));
721 }
722
723 soundDoseCallback->onNewCsdValue(currentCsd, newRecordsToReport);
724 }
725 }
726
getSoundDoseCallback() const727 sp<media::ISoundDoseCallback> SoundDoseManager::getSoundDoseCallback() const {
728 const std::lock_guard _l(mLock);
729 if (mSoundDose == nullptr) {
730 return nullptr;
731 }
732
733 return mSoundDose->mSoundDoseCallback;
734 }
735
onMomentaryExposure(float currentMel,audio_port_handle_t deviceId) const736 void SoundDoseManager::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const {
737 ALOGV("%s: Momentary exposure for device %d triggered: %f MEL", __func__, deviceId, currentMel);
738
739 {
740 const std::lock_guard _l(mLock);
741 if (!mEnabledCsd) {
742 return;
743 }
744
745 if (currentMel < mRs2UpperBound) {
746 return;
747 }
748 }
749
750 auto soundDoseCallback = getSoundDoseCallback();
751 if (soundDoseCallback != nullptr) {
752 soundDoseCallback->onMomentaryExposure(currentMel, deviceId);
753 }
754 }
755
resetReferencesForTest()756 void SoundDoseManager::resetReferencesForTest() {
757 mMelReporterCallback.clear();
758 }
759
getSoundDoseInterface(const sp<media::ISoundDoseCallback> & callback)760 sp<media::ISoundDose> SoundDoseManager::getSoundDoseInterface(
761 const sp<media::ISoundDoseCallback>& callback) {
762 ALOGV("%s: Register ISoundDoseCallback", __func__);
763
764 const std::lock_guard _l(mLock);
765 if (mSoundDose == nullptr) {
766 mSoundDose = sp<SoundDose>::make(this, callback);
767 }
768 return mSoundDose;
769 }
770
dump() const771 std::string SoundDoseManager::dump() const {
772 std::string output;
773 {
774 const std::lock_guard _l(mLock);
775 if (!mEnabledCsd) {
776 base::StringAppendF(&output, "CSD is disabled");
777 return output;
778 }
779 }
780
781 mMelAggregator->foreachCsd([&output](audio_utils::CsdRecord csdRecord) {
782 base::StringAppendF(&output,
783 "CSD %f with average MEL %f in interval [%" PRId64 ", %" PRId64 "]",
784 csdRecord.value, csdRecord.averageMel, csdRecord.timestamp,
785 csdRecord.timestamp + csdRecord.duration);
786 base::StringAppendF(&output, "\n");
787 });
788
789 base::StringAppendF(&output, "\nCached Mel Records:\n");
790 mMelAggregator->foreachCachedMel([&output](const audio_utils::MelRecord& melRecord) {
791 base::StringAppendF(&output, "Continuous MELs for portId=%d, ", melRecord.portId);
792 base::StringAppendF(&output, "starting at timestamp %" PRId64 ": ", melRecord.timestamp);
793
794 for (const auto& mel : melRecord.mels) {
795 base::StringAppendF(&output, "%.2f ", mel);
796 }
797 base::StringAppendF(&output, "\n");
798 });
799
800 return output;
801 }
802
getCachedMelRecordsSize() const803 size_t SoundDoseManager::getCachedMelRecordsSize() const {
804 return mMelAggregator->getCachedMelRecordsSize();
805 }
806
csdRecordToSoundDoseRecord(const audio_utils::CsdRecord & legacy)807 media::SoundDoseRecord SoundDoseManager::csdRecordToSoundDoseRecord(
808 const audio_utils::CsdRecord& legacy) {
809 media::SoundDoseRecord soundDoseRecord{};
810 soundDoseRecord.timestamp = legacy.timestamp;
811 soundDoseRecord.duration = legacy.duration;
812 soundDoseRecord.value = legacy.value;
813 soundDoseRecord.averageMel = legacy.averageMel;
814 return soundDoseRecord;
815 }
816
817 } // namespace android
818