From b82e6b724e45b1f88ed746c316b445b3bb8036b3 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 22 Nov 2019 17:25:04 -0800 Subject: [PATCH] AudioFlinger: implement device specific audio effects Add management of audio effects applied to a specific input or output audio device. A new class DeviceEffectProxy derived from Effectbase is added to represent an effect attached to a particular audio device type and address. This proxy manages one or more actual EffectModule instances automatically added to a playback or capture thread or directly to the audio HAL when the targeted audio device is used by an audio patch. A new DeviceEffectManager class is added to manage creation and release of DeviceEffectProxy instances and monitor creation and release of audio patches and create or release actual effect instances accordingly. Bug: 136294538 Test: make Change-Id: I23b9f9db4459136039c5ee327cf3b1aefa7db5af --- .../AudioDeviceTypeAddr.cpp | 10 + .../include/media/AudioDeviceTypeAddr.h | 2 + media/libaudiohal/impl/DeviceHalHidl.cpp | 33 ++ media/libaudiohal/impl/DeviceHalHidl.h | 3 + media/libaudiohal/impl/DeviceHalLocal.cpp | 11 + media/libaudiohal/impl/DeviceHalLocal.h | 3 + .../media/audiohal/DeviceHalInterface.h | 6 + services/audioflinger/Android.bp | 1 + services/audioflinger/AudioFlinger.cpp | 42 ++- services/audioflinger/AudioFlinger.h | 16 +- services/audioflinger/DeviceEffectManager.cpp | 277 ++++++++++++++ services/audioflinger/DeviceEffectManager.h | 203 +++++++++++ services/audioflinger/Effects.cpp | 338 +++++++++++++++++- services/audioflinger/Effects.h | 99 ++++- services/audioflinger/PatchPanel.cpp | 53 ++- services/audioflinger/PatchPanel.h | 57 ++- services/audioflinger/Threads.cpp | 5 +- .../managerdefault/AudioPolicyManager.cpp | 12 +- 18 files changed, 1127 insertions(+), 44 deletions(-) create mode 100644 services/audioflinger/DeviceEffectManager.cpp create mode 100644 services/audioflinger/DeviceEffectManager.h diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp index d3904676a1..b44043ae4b 100644 --- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp +++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp @@ -26,6 +26,16 @@ bool AudioDeviceTypeAddr::equals(const AudioDeviceTypeAddr& other) const { return mType == other.mType && mAddress == other.mAddress; } +bool AudioDeviceTypeAddr::operator<(const AudioDeviceTypeAddr& other) const { + if (mType < other.mType) return true; + if (mType > other.mType) return false; + + if (mAddress < other.mAddress) return true; + // if (mAddress > other.mAddress) return false; + + return false; +} + void AudioDeviceTypeAddr::reset() { mType = AUDIO_DEVICE_NONE; mAddress = ""; diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h index acc37ca964..60ea78e36d 100644 --- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h +++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h @@ -39,6 +39,8 @@ struct AudioDeviceTypeAddr : public Parcelable { AudioDeviceTypeAddr& operator= (const AudioDeviceTypeAddr&) = default; + bool operator<(const AudioDeviceTypeAddr& other) const; + void reset(); status_t readFromParcel(const Parcel *parcel) override; diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index 342ceb635f..7d0d83d567 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -28,6 +28,7 @@ #include #include "DeviceHalHidl.h" +#include "EffectHalHidl.h" #include "HidlUtils.h" #include "StreamHalHidl.h" #include "VersionUtils.h" @@ -43,6 +44,8 @@ namespace CPP_VERSION { using namespace ::android::hardware::audio::common::CPP_VERSION; using namespace ::android::hardware::audio::CPP_VERSION; +using EffectHalHidl = ::android::effect::CPP_VERSION::EffectHalHidl; + namespace { status_t deviceAddressFromHal( @@ -417,6 +420,36 @@ status_t DeviceHalHidl::getMicrophones(std::vector *micro } #endif +#if MAJOR_VERSION >= 6 +status_t DeviceHalHidl::addDeviceEffect( + audio_port_handle_t device, sp effect) { + if (mDevice == 0) return NO_INIT; + return processReturn("addDeviceEffect", mDevice->addDeviceEffect( + static_cast(device), + static_cast(effect.get())->effectId())); +} +#else +status_t DeviceHalHidl::addDeviceEffect( + audio_port_handle_t device __unused, sp effect __unused) { + return INVALID_OPERATION; +} +#endif + +#if MAJOR_VERSION >= 6 +status_t DeviceHalHidl::removeDeviceEffect( + audio_port_handle_t device, sp effect) { + if (mDevice == 0) return NO_INIT; + return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect( + static_cast(device), + static_cast(effect.get())->effectId())); +} +#else +status_t DeviceHalHidl::removeDeviceEffect( + audio_port_handle_t device __unused, sp effect __unused) { + return INVALID_OPERATION; +} +#endif + status_t DeviceHalHidl::dump(int fd) { if (mDevice == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h index f7d465ffb0..d342d4aaab 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -113,6 +113,9 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl // List microphones virtual status_t getMicrophones(std::vector *microphones); + status_t addDeviceEffect(audio_port_handle_t device, sp effect) override; + status_t removeDeviceEffect(audio_port_handle_t device, sp effect) override; + virtual status_t dump(int fd); private: diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp index dfbb6b2698..8021d9221a 100644 --- a/media/libaudiohal/impl/DeviceHalLocal.cpp +++ b/media/libaudiohal/impl/DeviceHalLocal.cpp @@ -206,6 +206,17 @@ status_t DeviceHalLocal::getMicrophones(std::vector *micr } #endif +// Local HAL implementation does not support effects +status_t DeviceHalLocal::addDeviceEffect( + audio_port_handle_t device __unused, sp effect __unused) { + return INVALID_OPERATION; +} + +status_t DeviceHalLocal::removeDeviceEffect( + audio_port_handle_t device __unused, sp effect __unused) { + return INVALID_OPERATION; +} + status_t DeviceHalLocal::dump(int fd) { return mDev->dump(mDev, fd); } diff --git a/media/libaudiohal/impl/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h index 36db72e132..d85e2a75ab 100644 --- a/media/libaudiohal/impl/DeviceHalLocal.h +++ b/media/libaudiohal/impl/DeviceHalLocal.h @@ -106,6 +106,9 @@ class DeviceHalLocal : public DeviceHalInterface // List microphones virtual status_t getMicrophones(std::vector *microphones); + status_t addDeviceEffect(audio_port_handle_t device, sp effect) override; + status_t removeDeviceEffect(audio_port_handle_t device, sp effect) override; + virtual status_t dump(int fd); void closeOutputStream(struct audio_stream_out *stream_out); diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h index 2200a7f1ce..1e04b21975 100644 --- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H #define ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H +#include #include #include #include @@ -111,6 +112,11 @@ class DeviceHalInterface : public RefBase // List microphones virtual status_t getMicrophones(std::vector *microphones) = 0; + virtual status_t addDeviceEffect( + audio_port_handle_t device, sp effect) = 0; + virtual status_t removeDeviceEffect( + audio_port_handle_t device, sp effect) = 0; + virtual status_t dump(int fd) = 0; protected: diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp index de8c7e79c7..c58360d5e7 100644 --- a/services/audioflinger/Android.bp +++ b/services/audioflinger/Android.bp @@ -9,6 +9,7 @@ cc_library_shared { "AudioStreamOut.cpp", "AudioWatchdog.cpp", "BufLog.cpp", + "DeviceEffectManager.cpp", "Effects.cpp", "FastCapture.cpp", "FastCaptureDumpState.cpp", diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0f756bbf01..6a2f0a6e0a 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -170,6 +170,7 @@ AudioFlinger::AudioFlinger() mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes), mGlobalEffectEnableTime(0), mPatchPanel(this), + mDeviceEffectManager(this), mSystemReady(false) { // unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum @@ -382,6 +383,24 @@ void AudioFlinger::onExternalVibrationStop(const sp& exte } } +status_t AudioFlinger::addEffectToHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect) { + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId); + if (audioHwDevice == nullptr) { + return NO_INIT; + } + return audioHwDevice->hwDevice()->addDeviceEffect(deviceId, effect); +} + +status_t AudioFlinger::removeEffectFromHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect) { + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId); + if (audioHwDevice == nullptr) { + return NO_INIT; + } + return audioHwDevice->hwDevice()->removeDeviceEffect(deviceId, effect); +} + static const char * const audio_interfaces[] = { AUDIO_HARDWARE_MODULE_ID_PRIMARY, AUDIO_HARDWARE_MODULE_ID_A2DP, @@ -558,6 +577,8 @@ status_t AudioFlinger::dump(int fd, const Vector& args) mPatchPanel.dump(fd); + mDeviceEffectManager.dump(fd); + // dump external setParameters auto dumpLogger = [fd](SimpleLog& logger, const char* name) { dprintf(fd, "\n%s setParameters:\n", name); @@ -3315,7 +3336,7 @@ sp AudioFlinger::createEffect( int32_t priority, audio_io_handle_t io, audio_session_t sessionId, - const AudioDeviceTypeAddr& device __unused, + const AudioDeviceTypeAddr& device, const String16& opPackageName, pid_t pid, status_t *status, @@ -3379,7 +3400,6 @@ sp AudioFlinger::createEffect( lStatus = BAD_VALUE; goto Exit; } - //TODO: add check on device ID when added to arguments } else { // general sessionId. @@ -3432,6 +3452,23 @@ sp AudioFlinger::createEffect( Mutex::Autolock _l(mLock); + if (sessionId == AUDIO_SESSION_DEVICE) { + sp client = registerPid(pid); + ALOGV("%s device type %d address %s", __func__, device.mType, device.getAddress()); + handle = mDeviceEffectManager.createEffect_l( + &desc, device, client, effectClient, mPatchPanel.patches_l(), + enabled, &lStatus); + if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) { + // remove local strong reference to Client with mClientLock held + Mutex::Autolock _cl(mClientLock); + client.clear(); + } else { + // handle must be valid here, but check again to be safe. + if (handle.get() != nullptr && id != nullptr) *id = handle->id(); + } + goto Register; + } + // If output is not specified try to find a matching audio session ID in one of the // output threads. // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX @@ -3526,6 +3563,7 @@ sp AudioFlinger::createEffect( } } +Register: if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) { // Check CPU and memory usage sp effect = handle->effect().promote(); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 5f55ddb7d4..bf6c20edfc 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -308,6 +308,12 @@ public: static int onExternalVibrationStart(const sp& externalVibration); static void onExternalVibrationStop(const sp& externalVibration); + + status_t addEffectToHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect); + status_t removeEffectFromHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect); + private: // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed. static const size_t kLogMemorySize = 400 * 1024; @@ -537,6 +543,10 @@ private: class EffectModule; class EffectHandle; class EffectChain; + class DeviceEffectProxy; + class DeviceEffectManager; + class PatchPanel; + class DeviceEffectManagerCallback; struct AudioStreamIn; struct TeePatch; @@ -574,9 +584,11 @@ using effect_buffer_t = int16_t; #include "Threads.h" +#include "PatchPanel.h" + #include "Effects.h" -#include "PatchPanel.h" +#include "DeviceEffectManager.h" // Find io handle by session id. // Preference is given to an io handle with a matching effect chain to session id. @@ -922,6 +934,8 @@ private: PatchPanel mPatchPanel; sp mEffectsFactoryHal; + DeviceEffectManager mDeviceEffectManager; + bool mSystemReady; SimpleLog mRejectedSetParameterLog; diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp new file mode 100644 index 0000000000..87a4c6ef37 --- /dev/null +++ b/services/audioflinger/DeviceEffectManager.cpp @@ -0,0 +1,277 @@ +/* +** +** 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 + +// ---------------------------------------------------------------------------- + + +namespace android { + +void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle, + const PatchPanel::Patch& patch) { + ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x", + __func__, handle, patch.mHalHandle, + patch.mAudioPatch.num_sinks, + patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0); + + mCommandThread->createAudioPatchCommand(handle, patch); +} + +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::releaseAudioPatch(audio_patch_handle_t handle) { + ALOGV("%s", __func__); + mCommandThread->releaseAudioPatchCommand(handle); +} + +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) { + sp effect; + sp handle; + status_t lStatus; + + lStatus = checkEffectCompatibility(descriptor); + if (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)); + } + // create effect handle and connect it to effect module + handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/); + lStatus = handle->initCheck(); + if (lStatus == NO_ERROR) { + lStatus = effect->addHandle(handle.get()); + if (lStatus == NO_ERROR) { + effect->init(patches); + mDeviceEffects.emplace(device, effect); + } + } + } + if (enabled != NULL) { + *enabled = (int)effect->isEnabled(); + } + *status = lStatus; + return handle; +} + +status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility( + const effect_descriptor_t *desc) { + + if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC + && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) { + ALOGW("%s() non pre/post processing device effect %s", __func__, desc->name); + 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; + sp effectsFactory = mAudioFlinger.getEffectsFactory(); + if (effectsFactory != 0) { + status = effectsFactory->createEffect( + pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect); + } + return status; +} + +void AudioFlinger::DeviceEffectManager::dump(int fd) { + const bool locked = dumpTryLock(mLock); + if (!locked) { + String8 result("DeviceEffectManager may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n")); + 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; +} + +// ----------- DeviceEffectManager::CommandThread implementation ---------- + + +AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread() +{ + Mutex::Autolock _l(mLock); + mCommands.clear(); +} + +void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef() +{ + run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO); +} + +bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop() +{ + mLock.lock(); + while (!exitPending()) + { + while (!mCommands.empty() && !exitPending()) { + sp command = mCommands.front(); + mCommands.pop_front(); + mLock.unlock(); + + switch (command->mCommand) { + case CREATE_AUDIO_PATCH: { + CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get(); + ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle); + mManager.onCreateAudioPatch(data->mHandle, data->mPatch); + } break; + case RELEASE_AUDIO_PATCH: { + ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get(); + ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle); + mManager.onReleaseAudioPatch(data->mHandle); + } break; + default: + ALOGW("CommandThread() unknown command %d", command->mCommand); + } + mLock.lock(); + } + + // At this stage we have either an empty command queue or the first command in the queue + // has a finite delay. So unless we are exiting it is safe to wait. + if (!exitPending()) { + ALOGV("CommandThread() going to sleep"); + mWaitWorkCV.wait(mLock); + } + } + mLock.unlock(); + return false; +} + +void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp command) { + Mutex::Autolock _l(mLock); + mCommands.push_back(command); + mWaitWorkCV.signal(); +} + +void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand( + audio_patch_handle_t handle, const PatchPanel::Patch& patch) +{ + sp command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch)); + ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle); + sendCommand(command); +} + +void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand( + audio_patch_handle_t handle) +{ + sp command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle)); + ALOGV("CommandThread() adding release patch"); + sendCommand(command); +} + +void AudioFlinger::DeviceEffectManager::CommandThread::exit() +{ + ALOGV("CommandThread::exit"); + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + // Note that we can call it from the thread loop if all other references have been released + // but it will safely return WOULD_BLOCK in this case + requestExitAndWait(); +} + +} // namespace android diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h new file mode 100644 index 0000000000..14ff14d932 --- /dev/null +++ b/services/audioflinger/DeviceEffectManager.h @@ -0,0 +1,203 @@ +/* +** +** 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. +*/ + +#ifndef INCLUDING_FROM_AUDIOFLINGER_H + #error This header file should only be included from AudioFlinger.h +#endif + +// DeviceEffectManager is concealed within AudioFlinger, their lifetimes are the same. +class DeviceEffectManager { +public: + explicit DeviceEffectManager(AudioFlinger* audioFlinger) + : mCommandThread(new CommandThread(*this)), mAudioFlinger(*audioFlinger), + mMyCallback(new DeviceEffectManagerCallback(this)) {} + + ~DeviceEffectManager() { + mCommandThread->exit(); + } + + sp createEffect_l(effect_descriptor_t *descriptor, + const AudioDeviceTypeAddr& device, + const sp& client, + const sp& effectClient, + const std::map& patches, + int *enabled, + status_t *status); + void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch); + void releaseAudioPatch(audio_patch_handle_t handle); + + size_t removeEffect(const sp& effect); + status_t createEffectHal(const effect_uuid_t *pEffectUuid, + int32_t sessionId, int32_t deviceId, + sp *effect); + status_t addEffectToHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId, + sp effect) { + return mAudioFlinger.addEffectToHal(deviceId, hwModuleId, effect); + }; + status_t removeEffectFromHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId, + sp effect) { + return mAudioFlinger.removeEffectFromHal(deviceId, hwModuleId, effect); + }; + + AudioFlinger& audioFlinger() const { return mAudioFlinger; } + + void dump(int fd); + +private: + + // Thread to execute create and release patch commands asynchronously. This is needed because + // PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service + // with mutex locked and effect management requires to call back into audio policy service + class Command; + class CommandThread : public Thread { + public: + + enum { + CREATE_AUDIO_PATCH, + RELEASE_AUDIO_PATCH, + }; + + CommandThread(DeviceEffectManager& manager) + : Thread(false), mManager(manager) {} + ~CommandThread() override; + + // Thread virtuals + void onFirstRef() override; + bool threadLoop() override; + + void exit(); + + void createAudioPatchCommand(audio_patch_handle_t handle, + const PatchPanel::Patch& patch); + void releaseAudioPatchCommand(audio_patch_handle_t handle); + + private: + class CommandData; + + // descriptor for requested tone playback event + class Command: public RefBase { + public: + Command() = default; + Command(int command, sp data) + : mCommand(command), mData(data) {} + + int mCommand = -1; + sp mData; + }; + + class CommandData: public RefBase { + public: + virtual ~CommandData() = default; + }; + + class CreateAudioPatchData : public CommandData { + public: + CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch) + : mHandle(handle), mPatch(patch) {} + + audio_patch_handle_t mHandle; + const PatchPanel::Patch mPatch; + }; + + class ReleaseAudioPatchData : public CommandData { + public: + ReleaseAudioPatchData(audio_patch_handle_t handle) + : mHandle(handle) {} + + audio_patch_handle_t mHandle; + }; + + void sendCommand(sp command); + + Mutex mLock; + Condition mWaitWorkCV; + std::deque > mCommands; // list of pending commands + DeviceEffectManager& mManager; + }; + + void onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch); + void onReleaseAudioPatch(audio_patch_handle_t handle); + + status_t checkEffectCompatibility(const effect_descriptor_t *desc); + + Mutex mLock; + sp mCommandThread; + AudioFlinger &mAudioFlinger; + const sp mMyCallback; + std::map> mDeviceEffects; +}; + +class DeviceEffectManagerCallback : public EffectCallbackInterface { +public: + DeviceEffectManagerCallback(DeviceEffectManager *manager) + : mManager(*manager) {} + + status_t createEffectHal(const effect_uuid_t *pEffectUuid, + int32_t sessionId, int32_t deviceId, + sp *effect) override { + return mManager.createEffectHal(pEffectUuid, sessionId, deviceId, effect); + } + status_t allocateHalBuffer(size_t size __unused, + sp* buffer __unused) override { return NO_ERROR; } + bool updateOrphanEffectChains(const sp& effect __unused) override { return false; } + + audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; } + bool isOutput() const override { return false; } + bool isOffload() const override { return false; } + bool isOffloadOrDirect() const override { return false; } + bool isOffloadOrMmap() const override { return false; } + + uint32_t sampleRate() const override { return 0; } + audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; } + uint32_t channelCount() const override { return 0; } + size_t frameCount() const override { return 0; } + uint32_t latency() const override { return 0; } + + status_t addEffectToHal(sp effect __unused) override { + return NO_ERROR; + } + status_t removeEffectFromHal(sp effect __unused) override { + return NO_ERROR; + } + + bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override; + void setVolumeForOutput(float left __unused, float right __unused) const override {} + + // check if effects should be suspended or restored when a given effect is enable or disabled + void checkSuspendOnEffectEnabled(const sp& effect __unused, + bool enabled __unused, bool threadLocked __unused) override {} + void resetVolume() override {} + uint32_t strategy() const override { return 0; } + int32_t activeTrackCnt() const override { return 0; } + void onEffectEnable(const sp& effect __unused) override {} + void onEffectDisable(const sp& effect __unused) override {} + + wp chain() const override { return nullptr; } + + int newEffectId() { return mManager.audioFlinger().nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); } + + status_t addEffectToHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect) { + return mManager.addEffectToHal(deviceId, hwModuleId, effect); + } + status_t removeEffectFromHal(audio_port_handle_t deviceId, + audio_module_handle_t hwModuleId, sp effect) { + return mManager.removeEffectFromHal(deviceId, hwModuleId, effect); + } +private: + DeviceEffectManager& mManager; +}; diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 4d37c943f1..ef2daa21df 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -512,7 +512,8 @@ AudioFlinger::EffectModule::EffectModule(const spcreateEffectHal( - &desc->uuid, sessionId, AUDIO_PORT_HANDLE_NONE, &mEffectInterface); + &desc->uuid, sessionId, deviceId, &mEffectInterface); if (mStatus != NO_ERROR) { return; } @@ -1602,7 +1603,7 @@ AudioFlinger::EffectHandle::EffectHandle(const sp& effect, mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL), mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false) { - ALOGV("constructor %p", this); + ALOGV("constructor %p client %p", this, client.get()); if (client == 0) { return; @@ -1790,12 +1791,13 @@ status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode, if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) { return INVALID_OPERATION; } - if (mClient == 0) { - return INVALID_OPERATION; - } // handle commands that are not forwarded transparently to effect engine if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) { + if (mClient == 0) { + return INVALID_OPERATION; + } + if (*replySize < sizeof(int)) { android_errorWriteLog(0x534e4554, "32095713"); return BAD_VALUE; @@ -2084,7 +2086,7 @@ status_t AudioFlinger::EffectChain::createEffect_l(sp& effect, bool pinned) { Mutex::Autolock _l(mLock); - effect = new EffectModule(mEffectCallback, desc, id, sessionId, pinned); + effect = new EffectModule(mEffectCallback, desc, id, sessionId, pinned, AUDIO_PORT_HANDLE_NONE); status_t lStatus = effect->status(); if (lStatus == NO_ERROR) { lStatus = addEffect_ll(effect); @@ -2918,4 +2920,326 @@ int32_t AudioFlinger::EffectChain::EffectCallback::activeTrackCnt() const { return c->activeTrackCnt(); } + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger::DeviceEffectProxy" + +status_t AudioFlinger::DeviceEffectProxy::setEnabled(bool enabled, bool fromHandle) +{ + status_t status = EffectBase::setEnabled(enabled, fromHandle); + Mutex::Autolock _l(mProxyLock); + if (status == NO_ERROR) { + for (auto& handle : mEffectHandles) { + if (enabled) { + status = handle.second->enable(); + } else { + status = handle.second->disable(); + } + } + } + ALOGV("%s enable %d status %d", __func__, enabled, status); + return status; +} + +status_t AudioFlinger::DeviceEffectProxy::init( + const std::map & patches) { +//For all audio patches +//If src or sink device match +//If the effect is HW accelerated +// if no corresponding effect module +// Create EffectModule: mHalEffect +//Create and attach EffectHandle +//If the effect is not HW accelerated and the patch sink or src is a mixer port +// Create Effect on patch input or output thread on session -1 +//Add EffectHandle to EffectHandle map of Effect Proxy: + ALOGV("%s device type %d address %s", __func__, mDevice.mType, mDevice.getAddress()); + status_t status = NO_ERROR; + for (auto &patch : patches) { + status = onCreatePatch(patch.first, patch.second); + ALOGV("%s onCreatePatch status %d", __func__, status); + if (status == BAD_VALUE) { + return status; + } + } + return status; +} + +status_t AudioFlinger::DeviceEffectProxy::onCreatePatch( + audio_patch_handle_t patchHandle, const AudioFlinger::PatchPanel::Patch& patch) { + status_t status = NAME_NOT_FOUND; + sp handle; + // only consider source[0] as this is the only "true" source of a patch + status = checkPort(patch, &patch.mAudioPatch.sources[0], &handle); + ALOGV("%s source checkPort status %d", __func__, status); + for (uint32_t i = 0; i < patch.mAudioPatch.num_sinks && status == NAME_NOT_FOUND; i++) { + status = checkPort(patch, &patch.mAudioPatch.sinks[i], &handle); + ALOGV("%s sink %d checkPort status %d", __func__, i, status); + } + if (status == NO_ERROR || status == ALREADY_EXISTS) { + Mutex::Autolock _l(mProxyLock); + mEffectHandles.emplace(patchHandle, handle); + } + ALOGW_IF(status == BAD_VALUE, + "%s cannot attach effect %s on patch %d", __func__, mDescriptor.name, patchHandle); + + return status; +} + +status_t AudioFlinger::DeviceEffectProxy::checkPort(const PatchPanel::Patch& patch, + const struct audio_port_config *port, sp *handle) { + + ALOGV("%s type %d device type %d address %s device ID %d patch.isSoftware() %d", + __func__, port->type, port->ext.device.type, + port->ext.device.address, port->id, patch.isSoftware()); + if (port->type != AUDIO_PORT_TYPE_DEVICE || port->ext.device.type != mDevice.mType + || port->ext.device.address != mDevice.mAddress) { + return NAME_NOT_FOUND; + } + status_t status = NAME_NOT_FOUND; + + if (mDescriptor.flags & EFFECT_FLAG_HW_ACC_TUNNEL) { + Mutex::Autolock _l(mProxyLock); + mDevicePort = *port; + mHalEffect = new EffectModule(mMyCallback, + const_cast(&mDescriptor), + mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE, + false /* pinned */, port->id); + if (audio_is_input_device(mDevice.mType)) { + mHalEffect->setInputDevice(mDevice); + } else { + mHalEffect->setDevices({mDevice}); + } + *handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/); + status = (*handle)->initCheck(); + if (status == OK) { + status = mHalEffect->addHandle((*handle).get()); + } else { + mHalEffect.clear(); + mDevicePort.id = AUDIO_PORT_HANDLE_NONE; + } + } else if (patch.isSoftware() || patch.thread().promote() != nullptr) { + sp thread; + if (audio_port_config_has_input_direction(port)) { + if (patch.isSoftware()) { + thread = patch.mRecord.thread(); + } else { + thread = patch.thread().promote(); + } + } else { + if (patch.isSoftware()) { + thread = patch.mPlayback.thread(); + } else { + thread = patch.thread().promote(); + } + } + int enabled; + *handle = thread->createEffect_l(nullptr, nullptr, 0, AUDIO_SESSION_DEVICE, + const_cast(&mDescriptor), + &enabled, &status, false); + ALOGV("%s thread->createEffect_l status %d", __func__, status); + } else { + status = BAD_VALUE; + } + if (status == NO_ERROR || status == ALREADY_EXISTS) { + if (isEnabled()) { + (*handle)->enable(); + } else { + (*handle)->disable(); + } + } + return status; +} + +void AudioFlinger::DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) { + Mutex::Autolock _l(mProxyLock); + mEffectHandles.erase(patchHandle); +} + + +size_t AudioFlinger::DeviceEffectProxy::removeEffect(const sp& effect) +{ + Mutex::Autolock _l(mProxyLock); + if (effect == mHalEffect) { + mHalEffect.clear(); + mDevicePort.id = AUDIO_PORT_HANDLE_NONE; + } + return mHalEffect == nullptr ? 0 : 1; +} + +status_t AudioFlinger::DeviceEffectProxy::addEffectToHal( + sp effect) { + if (mHalEffect == nullptr) { + return NO_INIT; + } + return mManagerCallback->addEffectToHal( + mDevicePort.id, mDevicePort.ext.device.hw_module, effect); +} + +status_t AudioFlinger::DeviceEffectProxy::removeEffectFromHal( + sp effect) { + if (mHalEffect == nullptr) { + return NO_INIT; + } + return mManagerCallback->removeEffectFromHal( + mDevicePort.id, mDevicePort.ext.device.hw_module, effect); +} + +bool AudioFlinger::DeviceEffectProxy::isOutput() const { + if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE) { + return mDevicePort.role == AUDIO_PORT_ROLE_SINK; + } + return true; +} + +uint32_t AudioFlinger::DeviceEffectProxy::sampleRate() const { + if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE && + (mDevicePort.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) != 0) { + return mDevicePort.sample_rate; + } + return DEFAULT_OUTPUT_SAMPLE_RATE; +} + +audio_channel_mask_t AudioFlinger::DeviceEffectProxy::channelMask() const { + if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE && + (mDevicePort.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) != 0) { + return mDevicePort.channel_mask; + } + return AUDIO_CHANNEL_OUT_STEREO; +} + +uint32_t AudioFlinger::DeviceEffectProxy::channelCount() const { + if (isOutput()) { + return audio_channel_count_from_out_mask(channelMask()); + } + return audio_channel_count_from_in_mask(channelMask()); +} + +void AudioFlinger::DeviceEffectProxy::dump(int fd, int spaces) { + const Vector args; + EffectBase::dump(fd, args); + + const bool locked = dumpTryLock(mProxyLock); + if (!locked) { + String8 result("DeviceEffectProxy may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + String8 outStr; + if (mHalEffect != nullptr) { + outStr.appendFormat("%*sHAL Effect Id: %d\n", spaces, "", mHalEffect->id()); + } else { + outStr.appendFormat("%*sNO HAL Effect\n", spaces, ""); + } + write(fd, outStr.string(), outStr.size()); + outStr.clear(); + + outStr.appendFormat("%*sSub Effects:\n", spaces, ""); + write(fd, outStr.string(), outStr.size()); + outStr.clear(); + + for (const auto& iter : mEffectHandles) { + outStr.appendFormat("%*sEffect for patch handle %d:\n", spaces + 2, "", iter.first); + write(fd, outStr.string(), outStr.size()); + outStr.clear(); + sp effect = iter.second->effect().promote(); + if (effect != nullptr) { + effect->dump(fd, args); + } + } + + if (locked) { + mLock.unlock(); + } +} + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger::DeviceEffectProxy::ProxyCallback" + +int AudioFlinger::DeviceEffectProxy::ProxyCallback::newEffectId() { + return mManagerCallback->newEffectId(); +} + + +bool AudioFlinger::DeviceEffectProxy::ProxyCallback::disconnectEffectHandle( + EffectHandle *handle, bool unpinIfLast) { + sp effectBase = handle->effect().promote(); + if (effectBase == nullptr) { + return false; + } + + sp effect = effectBase->asEffectModule(); + 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) { + sp proxy = mProxy.promote(); + if (proxy != nullptr) { + proxy->removeEffect(effect); + } + if (handle->enabled()) { + effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/); + } + } + return true; +} + +status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::createEffectHal( + const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId, + sp *effect) { + return mManagerCallback->createEffectHal(pEffectUuid, sessionId, deviceId, effect); +} + +status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal( + sp effect) { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return NO_INIT; + } + return proxy->addEffectToHal(effect); +} + +status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::removeEffectFromHal( + sp effect) { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return NO_INIT; + } + return proxy->addEffectToHal(effect); +} + +bool AudioFlinger::DeviceEffectProxy::ProxyCallback::isOutput() const { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return true; + } + return proxy->isOutput(); +} + +uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::sampleRate() const { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return DEFAULT_OUTPUT_SAMPLE_RATE; + } + return proxy->sampleRate(); +} + +audio_channel_mask_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelMask() const { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return AUDIO_CHANNEL_OUT_STEREO; + } + return proxy->channelMask(); +} + +uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelCount() const { + sp proxy = mProxy.promote(); + if (proxy == nullptr) { + return 2; + } + return proxy->channelCount(); +} + } // namespace android diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index ea51c2c267..40bb226cc1 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -33,7 +33,7 @@ public: virtual bool isOffload() const = 0; virtual bool isOffloadOrDirect() const = 0; virtual bool isOffloadOrMmap() const = 0; - virtual uint32_t sampleRate() const = 0; + virtual uint32_t sampleRate() const = 0; virtual audio_channel_mask_t channelMask() const = 0; virtual uint32_t channelCount() const = 0; virtual size_t frameCount() const = 0; @@ -159,6 +159,7 @@ public: status_t updatePolicyState(); virtual sp asEffectModule() { return nullptr; } + virtual sp asDeviceEffectProxy() { return nullptr; } void dump(int fd, const Vector& args); @@ -206,7 +207,8 @@ public: effect_descriptor_t *desc, int id, audio_session_t sessionId, - bool pinned); + bool pinned, + audio_port_handle_t deviceId); virtual ~EffectModule(); void process(); @@ -613,3 +615,96 @@ private: const sp mEffectCallback; }; + +class DeviceEffectProxy : public EffectBase { +public: + DeviceEffectProxy (const AudioDeviceTypeAddr& device, + const sp& callback, + effect_descriptor_t *desc, int id) + : EffectBase(callback, desc, id, AUDIO_SESSION_DEVICE, false), + mDevice(device), mManagerCallback(callback), + mMyCallback(new ProxyCallback(this, callback)) {} + + status_t setEnabled(bool enabled, bool fromHandle) override; + sp asDeviceEffectProxy() override { return this; } + + status_t init(const std::map& patches); + status_t onCreatePatch(audio_patch_handle_t patchHandle, const PatchPanel::Patch& patch); + void onReleasePatch(audio_patch_handle_t patchHandle); + + size_t removeEffect(const sp& effect); + + status_t addEffectToHal(sp effect); + status_t removeEffectFromHal(sp effect); + + const AudioDeviceTypeAddr& device() { return mDevice; }; + bool isOutput() const; + uint32_t sampleRate() const; + audio_channel_mask_t channelMask() const; + uint32_t channelCount() const; + + void dump(int fd, int spaces); + +private: + + class ProxyCallback : public EffectCallbackInterface { + public: + ProxyCallback(DeviceEffectProxy *proxy, + const sp& callback) + : mProxy(proxy), mManagerCallback(callback) {} + + status_t createEffectHal(const effect_uuid_t *pEffectUuid, + int32_t sessionId, int32_t deviceId, sp *effect) override; + status_t allocateHalBuffer(size_t size __unused, + sp* buffer __unused) override { return NO_ERROR; } + bool updateOrphanEffectChains(const sp& effect __unused) override { + return false; + } + + audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; } + bool isOutput() const override; + bool isOffload() const override { return false; } + bool isOffloadOrDirect() const override { return false; } + bool isOffloadOrMmap() const override { return false; } + + uint32_t sampleRate() const override; + audio_channel_mask_t channelMask() const override; + uint32_t channelCount() const override; + size_t frameCount() const override { return 0; } + uint32_t latency() const override { return 0; } + + status_t addEffectToHal(sp effect) override; + status_t removeEffectFromHal(sp effect) override; + + bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override; + void setVolumeForOutput(float left __unused, float right __unused) const override {} + + void checkSuspendOnEffectEnabled(const sp& effect __unused, + bool enabled __unused, bool threadLocked __unused) override {} + void resetVolume() override {} + uint32_t strategy() const override { return 0; } + int32_t activeTrackCnt() const override { return 0; } + void onEffectEnable(const sp& effect __unused) override {} + void onEffectDisable(const sp& effect __unused) override {} + + wp chain() const override { return nullptr; } + + int newEffectId(); + + private: + const wp mProxy; + const sp mManagerCallback; + }; + + status_t checkPort(const PatchPanel::Patch& patch, const struct audio_port_config *port, + sp *handle); + + const AudioDeviceTypeAddr mDevice; + const sp mManagerCallback; + const sp mMyCallback; + + Mutex mProxyLock; + std::map> mEffectHandles; // protected by mProxyLock + sp mHalEffect; // protected by mProxyLock + struct audio_port_config mDevicePort = { .id = AUDIO_PORT_HANDLE_NONE }; +}; diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index dbd761a366..5501856e04 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -170,8 +170,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } halHandle = removedPatch.mHalHandle; } - mPatches.erase(iter); - removeSoftwarePatchFromInsertedModules(*handle); + erasePatch(*handle); } } @@ -326,10 +325,14 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } } status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + if (status == NO_ERROR) { + newPatch.setThread(thread); + } + // remove stale audio patch with same input as sink if any for (auto& iter : mPatches) { if (iter.second.mAudioPatch.sinks[0].ext.mix.handle == thread->id()) { - mPatches.erase(iter.first); + erasePatch(iter.first); break; } } @@ -388,11 +391,14 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + if (status == NO_ERROR) { + newPatch.setThread(thread); + } // remove stale audio patch with same output as source if any for (auto& iter : mPatches) { if (iter.second.mAudioPatch.sources[0].ext.mix.handle == thread->id()) { - mPatches.erase(iter.first); + erasePatch(iter.first); break; } } @@ -406,11 +412,11 @@ exit: if (status == NO_ERROR) { *handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); newPatch.mHalHandle = halHandle; + mAudioFlinger.mDeviceEffectManager.createAudioPatch(*handle, newPatch); mPatches.insert(std::make_pair(*handle, std::move(newPatch))); if (insertedModule != AUDIO_MODULE_HANDLE_NONE) { addSoftwarePatchToInsertedModules(insertedModule, *handle); } - ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle); } else { newPatch.clearConnections(this); } @@ -634,8 +640,21 @@ status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const { // TODO: Consider table dump form for patches, just like tracks. - String8 result = String8::format("Patch %d: thread %p => thread %p", - myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get()); + String8 result = String8::format("Patch %d: %s (thread %p => thread %p)", + myHandle, isSoftware() ? "Software bridge between" : "No software bridge", + mRecord.const_thread().get(), mPlayback.const_thread().get()); + + bool hasSinkDevice = + mAudioPatch.num_sinks > 0 && mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE; + bool hasSourceDevice = + mAudioPatch.num_sources > 0 && mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE; + result.appendFormat(" thread %p %s (%d) first device type %08x", mThread.unsafe_get(), + hasSinkDevice ? "num sinks" : + (hasSourceDevice ? "num sources" : "no devices"), + hasSinkDevice ? mAudioPatch.num_sinks : + (hasSourceDevice ? mAudioPatch.num_sources : 0), + hasSinkDevice ? mAudioPatch.sinks[0].ext.device.type : + (hasSourceDevice ? mAudioPatch.sources[0].ext.device.type : 0)); // add latency if it exists double latencyMs; @@ -711,11 +730,16 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle status = BAD_VALUE; } - mPatches.erase(iter); - removeSoftwarePatchFromInsertedModules(handle); + erasePatch(handle); return status; } +void AudioFlinger::PatchPanel::erasePatch(audio_patch_handle_t handle) { + mPatches.erase(handle); + removeSoftwarePatchFromInsertedModules(handle); + mAudioFlinger.mDeviceEffectManager.releaseAudioPatch(handle); +} + /* List connected audio ports and they attributes */ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, struct audio_patch *patches __unused) @@ -799,16 +823,13 @@ void AudioFlinger::PatchPanel::dump(int fd) const String8 patchPanelDump; const char *indent = " "; - // Only dump software patches. bool headerPrinted = false; for (const auto& iter : mPatches) { - if (iter.second.isSoftware()) { - if (!headerPrinted) { - patchPanelDump += "\nSoftware patches:\n"; - headerPrinted = true; - } - patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string()); + if (!headerPrinted) { + patchPanelDump += "\nPatches:\n"; + headerPrinted = true; } + patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string()); } headerPrinted = false; diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 181e27c2ea..4e0d243f7e 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -76,13 +76,18 @@ public: void dump(int fd) const; -private: template - class Endpoint { + class Endpoint final { public: Endpoint() = default; Endpoint(const Endpoint&) = delete; - Endpoint& operator=(const Endpoint&) = delete; + Endpoint& operator=(const Endpoint& other) noexcept { + mThread = other.mThread; + mCloseThread = other.mCloseThread; + mHandle = other.mHandle; + mTrack = other.mTrack; + return *this; + } Endpoint(Endpoint&& other) noexcept { swap(other); } Endpoint& operator=(Endpoint&& other) noexcept { swap(other); @@ -98,8 +103,8 @@ private: return trackOrNull->initCheck(); } audio_patch_handle_t handle() const { return mHandle; } - sp thread() { return mThread; } - sp track() { return mTrack; } + sp thread() const { return mThread; } + sp track() const { return mTrack; } sp const_thread() const { return mThread; } sp const_track() const { return mTrack; } @@ -150,14 +155,36 @@ private: sp mTrack; }; - class Patch { + class Patch final { public: explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {} + Patch() = default; ~Patch(); - Patch(const Patch&) = delete; - Patch(Patch&&) = default; - Patch& operator=(const Patch&) = delete; - Patch& operator=(Patch&&) = default; + Patch(const Patch& other) noexcept { + mAudioPatch = other.mAudioPatch; + mHalHandle = other.mHalHandle; + mPlayback = other.mPlayback; + mRecord = other.mRecord; + mThread = other.mThread; + } + Patch(Patch&& other) noexcept { swap(other); } + Patch& operator=(Patch&& other) noexcept { + swap(other); + return *this; + } + + void swap(Patch &other) noexcept { + using std::swap; + swap(mAudioPatch, other.mAudioPatch); + swap(mHalHandle, other.mHalHandle); + swap(mPlayback, other.mPlayback); + swap(mRecord, other.mRecord); + swap(mThread, other.mThread); + } + + friend void swap(Patch &a, Patch &b) noexcept { + a.swap(b); + } status_t createConnections(PatchPanel *panel); void clearConnections(PatchPanel *panel); @@ -165,6 +192,9 @@ private: return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE || mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; } + void setThread(sp thread) { mThread = thread; } + wp thread() const { return mThread; } + // returns the latency of the patch (from record to playback). status_t getLatencyMs(double *latencyMs) const; @@ -182,13 +212,20 @@ private: Endpoint mPlayback; // connects source device to record thread input Endpoint mRecord; + + wp mThread; }; + // Call with AudioFlinger mLock held + std::map& patches_l() { return mPatches; } + +private: AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module); sp findHwDeviceByModule(audio_module_handle_t module); void addSoftwarePatchToInsertedModules( audio_module_handle_t module, audio_patch_handle_t handle); void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle); + void erasePatch(audio_patch_handle_t handle); AudioFlinger &mAudioFlinger; std::map mPatches; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index b9afba891b..87b72fb3c4 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1423,7 +1423,10 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle, if (effectBase == nullptr) { return; } - effect = static_cast(effectBase.get()); + effect = effectBase->asEffectModule(); + if (effect == nullptr) { + return; + } // restore suspended effects if the disconnected handle was enabled and the last one. remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast); if (remove) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e198bebbbf..a220bebdb5 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2770,12 +2770,14 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, int session, int id) { - ssize_t index = mOutputs.indexOfKey(io); - if (index < 0) { - index = mInputs.indexOfKey(io); + if (session != AUDIO_SESSION_DEVICE) { + ssize_t index = mOutputs.indexOfKey(io); if (index < 0) { - ALOGW("registerEffect() unknown io %d", io); - return INVALID_OPERATION; + index = mInputs.indexOfKey(io); + if (index < 0) { + ALOGW("registerEffect() unknown io %d", io); + return INVALID_OPERATION; + } } } return mEffects.registerEffect(desc, io, session, id,