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
Merged-In: I23b9f9db4459136039c5ee327cf3b1aefa7db5af
gugelfrei
Eric Laurent 5 years ago
parent 249dba6eb7
commit 9b2064c3af

@ -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 = "";

@ -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;

@ -28,6 +28,7 @@
#include <common/all-versions/VersionUtils.h>
#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(
@ -385,6 +388,36 @@ status_t DeviceHalHidl::getMicrophones(std::vector<media::MicrophoneInfo> *micro
}
#endif
#if MAJOR_VERSION >= 6
status_t DeviceHalHidl::addDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) {
if (mDevice == 0) return NO_INIT;
return processReturn("addDeviceEffect", mDevice->addDeviceEffect(
static_cast<AudioPortHandle>(device),
static_cast<EffectHalHidl*>(effect.get())->effectId()));
}
#else
status_t DeviceHalHidl::addDeviceEffect(
audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
return INVALID_OPERATION;
}
#endif
#if MAJOR_VERSION >= 6
status_t DeviceHalHidl::removeDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) {
if (mDevice == 0) return NO_INIT;
return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect(
static_cast<AudioPortHandle>(device),
static_cast<EffectHalHidl*>(effect.get())->effectId()));
}
#else
status_t DeviceHalHidl::removeDeviceEffect(
audio_port_handle_t device __unused, sp<EffectHalInterface> 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);

@ -113,6 +113,9 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
virtual status_t dump(int fd);
private:

@ -206,6 +206,17 @@ status_t DeviceHalLocal::getMicrophones(std::vector<media::MicrophoneInfo> *micr
}
#endif
// Local HAL implementation does not support effects
status_t DeviceHalLocal::addDeviceEffect(
audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
return INVALID_OPERATION;
}
status_t DeviceHalLocal::removeDeviceEffect(
audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
return INVALID_OPERATION;
}
status_t DeviceHalLocal::dump(int fd) {
return mDev->dump(mDev, fd);
}

@ -106,6 +106,9 @@ class DeviceHalLocal : public DeviceHalInterface
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
virtual status_t dump(int fd);
void closeOutputStream(struct audio_stream_out *stream_out);

@ -17,6 +17,7 @@
#ifndef ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
#define ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
#include <media/audiohal/EffectHalInterface.h>
#include <media/MicrophoneInfo.h>
#include <system/audio.h>
#include <utils/Errors.h>
@ -111,6 +112,11 @@ class DeviceHalInterface : public RefBase
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
virtual status_t addDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
virtual status_t removeDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
virtual status_t dump(int fd) = 0;
protected:

@ -9,6 +9,7 @@ cc_library_shared {
"AudioStreamOut.cpp",
"AudioWatchdog.cpp",
"BufLog.cpp",
"DeviceEffectManager.cpp",
"Effects.cpp",
"FastCapture.cpp",
"FastCaptureDumpState.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<os::ExternalVibration>& exte
}
}
status_t AudioFlinger::addEffectToHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> 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<EffectHalInterface> 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<String16>& 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);
@ -3294,7 +3315,7 @@ sp<IEffect> 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,
@ -3358,7 +3379,6 @@ sp<IEffect> AudioFlinger::createEffect(
lStatus = BAD_VALUE;
goto Exit;
}
//TODO: add check on device ID when added to arguments
} else {
// general sessionId.
@ -3411,6 +3431,23 @@ sp<IEffect> AudioFlinger::createEffect(
Mutex::Autolock _l(mLock);
if (sessionId == AUDIO_SESSION_DEVICE) {
sp<Client> 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
@ -3505,6 +3542,7 @@ sp<IEffect> AudioFlinger::createEffect(
}
}
Register:
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
// Check CPU and memory usage
sp<EffectBase> effect = handle->effect().promote();

@ -308,6 +308,12 @@ public:
static int onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration);
static void onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration);
status_t addEffectToHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect);
status_t removeEffectFromHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> 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<EffectsFactoryHalInterface> mEffectsFactoryHal;
DeviceEffectManager mDeviceEffectManager;
bool mSystemReady;
SimpleLog mRejectedSetParameterLog;

@ -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 <utils/Log.h>
#include <audio_utils/primitives.h>
#include "AudioFlinger.h"
#include <media/audiohal/EffectsFactoryHalInterface.h>
// ----------------------------------------------------------------------------
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::EffectHandle> AudioFlinger::DeviceEffectManager::createEffect_l(
effect_descriptor_t *descriptor,
const AudioDeviceTypeAddr& device,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
int *enabled,
status_t *status) {
sp<DeviceEffectProxy> effect;
sp<EffectHandle> 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<EffectHalInterface> *effect) {
status_t status = NO_INIT;
sp<EffectsFactoryHalInterface> 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<DeviceEffectProxy>& effect)
{
Mutex::Autolock _l(mLock);
mDeviceEffects.erase(effect->device());
return mDeviceEffects.size();
}
bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle(
EffectHandle *handle, bool unpinIfLast) {
sp<EffectBase> effectBase = handle->effect().promote();
if (effectBase == nullptr) {
return false;
}
sp<DeviceEffectProxy> 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> 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> 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> 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> 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

@ -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<EffectHandle> createEffect_l(effect_descriptor_t *descriptor,
const AudioDeviceTypeAddr& device,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
const std::map<audio_patch_handle_t, PatchPanel::Patch>& 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<DeviceEffectProxy>& effect);
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
int32_t sessionId, int32_t deviceId,
sp<EffectHalInterface> *effect);
status_t addEffectToHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
sp<EffectHalInterface> effect) {
return mAudioFlinger.addEffectToHal(deviceId, hwModuleId, effect);
};
status_t removeEffectFromHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
sp<EffectHalInterface> 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<CommandData> data)
: mCommand(command), mData(data) {}
int mCommand = -1;
sp<CommandData> 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> command);
Mutex mLock;
Condition mWaitWorkCV;
std::deque <sp<Command>> 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<CommandThread> mCommandThread;
AudioFlinger &mAudioFlinger;
const sp<DeviceEffectManagerCallback> mMyCallback;
std::map<AudioDeviceTypeAddr, sp<DeviceEffectProxy>> 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<EffectHalInterface> *effect) override {
return mManager.createEffectHal(pEffectUuid, sessionId, deviceId, effect);
}
status_t allocateHalBuffer(size_t size __unused,
sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
bool updateOrphanEffectChains(const sp<EffectBase>& 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<EffectHalInterface> effect __unused) override {
return NO_ERROR;
}
status_t removeEffectFromHal(sp<EffectHalInterface> 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<EffectBase>& 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<EffectBase>& effect __unused) override {}
void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
wp<EffectChain> 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<EffectHalInterface> effect) {
return mManager.addEffectToHal(deviceId, hwModuleId, effect);
}
status_t removeEffectFromHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
return mManager.removeEffectFromHal(deviceId, hwModuleId, effect);
}
private:
DeviceEffectManager& mManager;
};

@ -512,7 +512,8 @@ AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackIn
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
bool pinned,
audio_port_handle_t deviceId)
: EffectBase(callback, desc, id, sessionId, pinned),
// clear mConfig to ensure consistent initial value of buffer framecount
// in case buffers are associated by setInBuffer() or setOutBuffer()
@ -531,7 +532,7 @@ AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackIn
// create effect engine from effect factory
mStatus = callback->createEffectHal(
&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<EffectBase>& 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;
@ -2085,7 +2087,7 @@ status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& 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);
@ -2919,4 +2921,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 <audio_patch_handle_t, PatchPanel::Patch>& 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<EffectHandle> 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 <EffectHandle> *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<effect_descriptor_t *>(&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 <ThreadBase> 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<effect_descriptor_t *>(&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<EffectModule>& 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<EffectHalInterface> effect) {
if (mHalEffect == nullptr) {
return NO_INIT;
}
return mManagerCallback->addEffectToHal(
mDevicePort.id, mDevicePort.ext.device.hw_module, effect);
}
status_t AudioFlinger::DeviceEffectProxy::removeEffectFromHal(
sp<EffectHalInterface> 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<String16> 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<EffectBase> 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> effectBase = handle->effect().promote();
if (effectBase == nullptr) {
return false;
}
sp<EffectModule> 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<DeviceEffectProxy> 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<EffectHalInterface> *effect) {
return mManagerCallback->createEffectHal(pEffectUuid, sessionId, deviceId, effect);
}
status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal(
sp<EffectHalInterface> effect) {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return NO_INIT;
}
return proxy->addEffectToHal(effect);
}
status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::removeEffectFromHal(
sp<EffectHalInterface> effect) {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return NO_INIT;
}
return proxy->addEffectToHal(effect);
}
bool AudioFlinger::DeviceEffectProxy::ProxyCallback::isOutput() const {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return true;
}
return proxy->isOutput();
}
uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::sampleRate() const {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return DEFAULT_OUTPUT_SAMPLE_RATE;
}
return proxy->sampleRate();
}
audio_channel_mask_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelMask() const {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return AUDIO_CHANNEL_OUT_STEREO;
}
return proxy->channelMask();
}
uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelCount() const {
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return 2;
}
return proxy->channelCount();
}
} // namespace android

@ -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<EffectModule> asEffectModule() { return nullptr; }
virtual sp<DeviceEffectProxy> asDeviceEffectProxy() { return nullptr; }
void dump(int fd, const Vector<String16>& 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<EffectCallback> mEffectCallback;
};
class DeviceEffectProxy : public EffectBase {
public:
DeviceEffectProxy (const AudioDeviceTypeAddr& device,
const sp<DeviceEffectManagerCallback>& 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<DeviceEffectProxy> asDeviceEffectProxy() override { return this; }
status_t init(const std::map<audio_patch_handle_t, PatchPanel::Patch>& 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<EffectModule>& effect);
status_t addEffectToHal(sp<EffectHalInterface> effect);
status_t removeEffectFromHal(sp<EffectHalInterface> 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<DeviceEffectManagerCallback>& callback)
: mProxy(proxy), mManagerCallback(callback) {}
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
status_t allocateHalBuffer(size_t size __unused,
sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
bool updateOrphanEffectChains(const sp<EffectBase>& 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<EffectHalInterface> effect) override;
status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
void setVolumeForOutput(float left __unused, float right __unused) const override {}
void checkSuspendOnEffectEnabled(const sp<EffectBase>& 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<EffectBase>& effect __unused) override {}
void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
wp<EffectChain> chain() const override { return nullptr; }
int newEffectId();
private:
const wp<DeviceEffectProxy> mProxy;
const sp<DeviceEffectManagerCallback> mManagerCallback;
};
status_t checkPort(const PatchPanel::Patch& patch, const struct audio_port_config *port,
sp<EffectHandle> *handle);
const AudioDeviceTypeAddr mDevice;
const sp<DeviceEffectManagerCallback> mManagerCallback;
const sp<ProxyCallback> mMyCallback;
Mutex mProxyLock;
std::map<audio_patch_handle_t, sp<EffectHandle>> mEffectHandles; // protected by mProxyLock
sp<EffectModule> mHalEffect; // protected by mProxyLock
struct audio_port_config mDevicePort = { .id = AUDIO_PORT_HANDLE_NONE };
};

@ -169,8 +169,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
}
mPatches.erase(iter);
removeSoftwarePatchFromInsertedModules(*handle);
erasePatch(*handle);
}
}
@ -325,10 +324,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;
}
}
@ -387,11 +390,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;
}
}
@ -405,11 +411,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);
}
@ -633,8 +639,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;
@ -710,11 +729,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)
@ -798,16 +822,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;

@ -76,13 +76,18 @@ public:
void dump(int fd) const;
private:
template<typename ThreadType, typename TrackType>
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<ThreadType> thread() { return mThread; }
sp<TrackType> track() { return mTrack; }
sp<ThreadType> thread() const { return mThread; }
sp<TrackType> track() const { return mTrack; }
sp<const ThreadType> const_thread() const { return mThread; }
sp<const TrackType> const_track() const { return mTrack; }
@ -150,14 +155,36 @@ private:
sp<TrackType> 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<ThreadBase> thread) { mThread = thread; }
wp<ThreadBase> 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<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
// connects source device to record thread input
Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
wp<ThreadBase> mThread;
};
// Call with AudioFlinger mLock held
std::map<audio_patch_handle_t, Patch>& patches_l() { return mPatches; }
private:
AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
sp<DeviceHalInterface> 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<audio_patch_handle_t, Patch> mPatches;

@ -1423,7 +1423,10 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
if (effectBase == nullptr) {
return;
}
effect = static_cast<EffectModule *>(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) {

@ -2756,12 +2756,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,

Loading…
Cancel
Save