/* ** ** Copyright 2019, 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_TAG "AudioFlinger::DeviceEffectManager" //#define LOG_NDEBUG 0 #include #include #include "AudioFlinger.h" #include "EffectConfiguration.h" #include // ---------------------------------------------------------------------------- namespace android { using detail::AudioHalVersionInfo; using media::IEffectClient; void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch) { ALOGV("%s handle %d mHalHandle %d device sink %08x", __func__, handle, patch.mHalHandle, patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0); Mutex::Autolock _l(mLock); for (auto& effect : mDeviceEffects) { status_t status = effect.second->onCreatePatch(handle, patch); ALOGV("%s Effect onCreatePatch status %d", __func__, status); ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status); } } void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) { ALOGV("%s", __func__); Mutex::Autolock _l(mLock); for (auto& effect : mDeviceEffects) { effect.second->onReleasePatch(handle); } } // DeviceEffectManager::createEffect_l() must be called with AudioFlinger::mLock held sp AudioFlinger::DeviceEffectManager::createEffect_l( effect_descriptor_t *descriptor, const AudioDeviceTypeAddr& device, const sp& client, const sp& effectClient, const std::map& patches, int *enabled, status_t *status, bool probe, bool notifyFramesProcessed) { sp effect; sp handle; status_t lStatus; lStatus = checkEffectCompatibility(descriptor); if (probe || lStatus != NO_ERROR) { *status = lStatus; return handle; } { Mutex::Autolock _l(mLock); auto iter = mDeviceEffects.find(device); if (iter != mDeviceEffects.end()) { effect = iter->second; } else { effect = new DeviceEffectProxy(device, mMyCallback, descriptor, mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT), notifyFramesProcessed); } // create effect handle and connect it to effect module handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/, notifyFramesProcessed); lStatus = handle->initCheck(); if (lStatus == NO_ERROR) { lStatus = effect->addHandle(handle.get()); if (lStatus == NO_ERROR) { lStatus = effect->init(patches); if (lStatus == NAME_NOT_FOUND) { lStatus = NO_ERROR; } if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) { mDeviceEffects.emplace(device, effect); } } } } if (enabled != nullptr) { *enabled = (int)effect->isEnabled(); } *status = lStatus; return handle; } status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility( const effect_descriptor_t *desc) { const sp effectsFactory = audioflinger::EffectConfiguration::getEffectsFactoryHal(); if (effectsFactory == nullptr) { return BAD_VALUE; } static const AudioHalVersionInfo sMinDeviceEffectHalVersion = AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 6, 0); static const AudioHalVersionInfo halVersion = audioflinger::EffectConfiguration::getAudioHalVersionInfo(); // We can trust AIDL generated AudioHalVersionInfo comparison operator (based on std::tie) as // long as the type, major and minor sequence doesn't change in the definition. if (((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) || halVersion < sMinDeviceEffectHalVersion) { ALOGW("%s() non pre/post processing device effect %s or incompatible API version %s", __func__, desc->name, halVersion.toString().c_str()); return BAD_VALUE; } return NO_ERROR; } status_t AudioFlinger::DeviceEffectManager::createEffectHal( const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId, sp *effect) { status_t status = NO_INIT; const sp effectsFactory = audioflinger::EffectConfiguration::getEffectsFactoryHal(); if (effectsFactory != 0) { status = effectsFactory->createEffect( pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect); } return status; } void AudioFlinger::DeviceEffectManager::dump(int fd) NO_THREAD_SAFETY_ANALYSIS // conditional try lock { const bool locked = dumpTryLock(mLock); if (!locked) { String8 result("DeviceEffectManager may be deadlocked\n"); write(fd, result.string(), result.size()); } String8 heading("\nDevice Effects:\n"); write(fd, heading.string(), heading.size()); for (const auto& iter : mDeviceEffects) { String8 outStr; outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "", ::android::toString(iter.first.mType).c_str(), iter.first.getAddress()); write(fd, outStr.string(), outStr.size()); iter.second->dump(fd, 4); } if (locked) { mLock.unlock(); } } size_t AudioFlinger::DeviceEffectManager::removeEffect(const sp& effect) { Mutex::Autolock _l(mLock); mDeviceEffects.erase(effect->device()); return mDeviceEffects.size(); } bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle( EffectHandle *handle, bool unpinIfLast) { sp effectBase = handle->effect().promote(); if (effectBase == nullptr) { return false; } sp effect = effectBase->asDeviceEffectProxy(); if (effect == nullptr) { return false; } // restore suspended effects if the disconnected handle was enabled and the last one. bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast); if (remove) { mManager.removeEffect(effect); if (handle->enabled()) { effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/); } } return true; } } // namespace android