audiopolicy: effects: preprocessing session not attached on right input
Bug: 267799634
Test: make
When an audio record is using a preferred device, and when it attaches
an effect to its session, the effect may not be attached to the right input.
Change-Id: I9e80cfde000a8a46fd21b19c4588c631f8512e3a
Signed-off-by: François Gaffie <francois.gaffie@renault.com>
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index ad2ba21..64193ef 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2226,6 +2226,9 @@
}
if (removed) {
removedEffects = purgeStaleEffects_l();
+ std::vector< sp<IAfEffectModule> > removedOrphanEffects = purgeOrphanEffectChains_l();
+ removedEffects.insert(removedEffects.end(), removedOrphanEffects.begin(),
+ removedOrphanEffects.end());
}
}
for (auto& effect : removedEffects) {
@@ -3716,6 +3719,42 @@
return removedEffects;
}
+std::vector< sp<IAfEffectModule> > AudioFlinger::purgeOrphanEffectChains_l()
+{
+ ALOGV("purging stale effects from orphan chains");
+ std::vector< sp<IAfEffectModule> > removedEffects;
+ for (size_t index = 0; index < mOrphanEffectChains.size(); index++) {
+ sp<IAfEffectChain> chain = mOrphanEffectChains.valueAt(index);
+ audio_session_t session = mOrphanEffectChains.keyAt(index);
+ if (session == AUDIO_SESSION_OUTPUT_MIX || session == AUDIO_SESSION_DEVICE
+ || session == AUDIO_SESSION_OUTPUT_STAGE) {
+ continue;
+ }
+ size_t numSessionRefs = mAudioSessionRefs.size();
+ bool found = false;
+ for (size_t k = 0; k < numSessionRefs; k++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(k);
+ if (ref->mSessionid == session) {
+ ALOGV(" session %d still exists for %d with %d refs", session, ref->mPid,
+ ref->mCnt);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ for (size_t i = 0; i < chain->numberOfEffects(); i++) {
+ sp<IAfEffectModule> effect = chain->getEffectModule(i);
+ removedEffects.push_back(effect);
+ }
+ }
+ }
+ for (auto& effect : removedEffects) {
+ effect->unPin();
+ updateOrphanEffectChains_l(effect);
+ }
+ return removedEffects;
+}
+
// dumpToThreadLog_l() must be called with AudioFlinger::mLock held
void AudioFlinger::dumpToThreadLog_l(const sp<ThreadBase> &thread)
{
@@ -4449,24 +4488,42 @@
return lStatus;
}
-status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
+status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ audio_io_handle_t dstIo)
+NO_THREAD_SAFETY_ANALYSIS
{
- ALOGV("moveEffects() session %d, srcOutput %d, dstOutput %d",
- sessionId, srcOutput, dstOutput);
+ ALOGV("%s() session %d, srcIo %d, dstIo %d", __func__, sessionId, srcIo, dstIo);
Mutex::Autolock _l(mLock);
- if (srcOutput == dstOutput) {
- ALOGW("moveEffects() same dst and src outputs %d", dstOutput);
+ if (srcIo == dstIo) {
+ ALOGW("%s() same dst and src outputs %d", __func__, dstIo);
return NO_ERROR;
}
- PlaybackThread *srcThread = checkPlaybackThread_l(srcOutput);
- if (srcThread == NULL) {
- ALOGW("moveEffects() bad srcOutput %d", srcOutput);
+ RecordThread *srcRecordThread = checkRecordThread_l(srcIo);
+ RecordThread *dstRecordThread = checkRecordThread_l(dstIo);
+ if (srcRecordThread != nullptr || dstRecordThread != nullptr) {
+ if (srcRecordThread != nullptr) {
+ srcRecordThread->mLock.lock();
+ }
+ if (dstRecordThread != nullptr) {
+ dstRecordThread->mLock.lock();
+ }
+ status_t ret = moveEffectChain_l(sessionId, srcRecordThread, dstRecordThread);
+ if (srcRecordThread != nullptr) {
+ srcRecordThread->mLock.unlock();
+ }
+ if (dstRecordThread != nullptr) {
+ dstRecordThread->mLock.unlock();
+ }
+ return ret;
+ }
+ PlaybackThread *srcThread = checkPlaybackThread_l(srcIo);
+ if (srcThread == nullptr) {
+ ALOGW("%s() bad srcIo %d", __func__, srcIo);
return BAD_VALUE;
}
- PlaybackThread *dstThread = checkPlaybackThread_l(dstOutput);
- if (dstThread == NULL) {
- ALOGW("moveEffects() bad dstOutput %d", dstOutput);
+ PlaybackThread *dstThread = checkPlaybackThread_l(dstIo);
+ if (dstThread == nullptr) {
+ ALOGW("%s() bad dstIo %d", __func__, dstIo);
return BAD_VALUE;
}
@@ -4604,6 +4661,50 @@
return status;
}
+
+// moveEffectChain_l must be called with both srcThread (if not null) and dstThread (if not null)
+// mLocks held
+status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId,
+ RecordThread *srcThread,
+ RecordThread *dstThread)
+NO_THREAD_SAFETY_ANALYSIS // requires srcThread and dstThread locks
+{
+ sp<IAfEffectChain> chain = nullptr;
+ if (srcThread != 0) {
+ Vector< sp<IAfEffectChain> > effectChains = srcThread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ if (effectChains[i]->sessionId() == sessionId) {
+ chain = effectChains[i];
+ break;
+ }
+ }
+ ALOGV_IF(effectChains.size() == 0, "%s: no effect chain on io=%d", __func__,
+ srcThread->id());
+ if (chain == nullptr) {
+ ALOGE("%s wrong session id %d", __func__, sessionId);
+ return BAD_VALUE;
+ }
+ ALOGV("%s: removing effect chain for session=%d io=%d", __func__, sessionId,
+ srcThread->id());
+ srcThread->removeEffectChain_l(chain);
+ } else {
+ chain = getOrphanEffectChain_l(sessionId);
+ if (chain == nullptr) {
+ ALOGE("%s: no orphan effect chain found for session=%d", __func__, sessionId);
+ return BAD_VALUE;
+ }
+ }
+ if (dstThread != 0) {
+ ALOGV("%s: adding effect chain for session=%d on io=%d", __func__, sessionId,
+ dstThread->id());
+ dstThread->addEffectChain_l(chain);
+ return NO_ERROR;
+ }
+ ALOGV("%s: parking to orphan effect chain for session=%d", __func__, sessionId);
+ putOrphanEffectChain_l(chain);
+ return NO_ERROR;
+}
+
status_t AudioFlinger::moveAuxEffectToIo(int EffectId,
const sp<PlaybackThread>& dstThread,
sp<PlaybackThread> *srcThread)
@@ -4720,6 +4821,11 @@
bool AudioFlinger::updateOrphanEffectChains(const sp<IAfEffectModule>& effect)
{
Mutex::Autolock _l(mLock);
+ return updateOrphanEffectChains_l(effect);
+}
+
+bool AudioFlinger::updateOrphanEffectChains_l(const sp<IAfEffectModule>& effect)
+{
audio_session_t session = effect->sessionId();
ssize_t index = mOrphanEffectChains.indexOfKey(session);
ALOGV("updateOrphanEffectChains session %d index %zd", session, index);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e2d340b..3431177 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -791,6 +791,9 @@
status_t moveEffectChain_l(audio_session_t sessionId,
PlaybackThread *srcThread,
PlaybackThread *dstThread);
+ status_t moveEffectChain_l(audio_session_t sessionId,
+ RecordThread *srcThread,
+ RecordThread *dstThread);
status_t moveAuxEffectToIo(int EffectId,
const sp<PlaybackThread>& dstThread,
@@ -840,6 +843,9 @@
private:
std::vector< sp<IAfEffectModule> > purgeStaleEffects_l();
+ std::vector< sp<IAfEffectModule> > purgeOrphanEffectChains_l();
+ bool updateOrphanEffectChains_l(const sp<IAfEffectModule>& effect);
+
void broadcastParametersToRecordThreads_l(const String8& keyValuePairs);
void updateOutDevicesForRecordThreads_l(const DeviceDescriptorBaseVector& devices);
void forwardParametersToDownstreamPatches_l(
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index 59eee52..d40bbcb 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -25,6 +25,10 @@
namespace android {
+class AudioInputCollection;
+class AudioInputDescriptor;
+class AudioPolicyClientInterface;
+
class EffectDescriptor : public RefBase
{
public:
@@ -40,6 +44,8 @@
int mId; // effect unique ID
audio_io_handle_t mIo; // io the effect is attached to
+ bool mIsOrphan = false; // on creation, effect is not yet attached but not yet orphan
+ bool mEnabledWhenMoved = false; // Backup enabled state before being moved
audio_session_t mSession; // audio session the effect is on
effect_descriptor_t mDesc; // effect descriptor
bool mEnabled; // enabled state: CPU load being used or not
@@ -69,12 +75,28 @@
void moveEffects(audio_session_t session,
audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput);
+ audio_io_handle_t dstOutput,
+ AudioPolicyClientInterface *clientInterface);
void moveEffects(const std::vector<int>& ids, audio_io_handle_t dstOutput);
+ void moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo, audio_io_handle_t dstIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *clientInterface);
+ void moveEffectsForIo(audio_session_t sessionId, audio_io_handle_t dstIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *mClientInterface);
+ void putOrphanEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ const AudioInputCollection *inputs, AudioPolicyClientInterface *clientInterface);
+ /**
+ * @brief Checks if an effect session was already attached to an io handle and return it if
+ * found. Check only for a given effect type if effectType is not null or for any effect
+ * otherwise.
+ * @param sessionId to consider.
+ * @param effectType to consider.
+ * @return ioHandle if found, AUDIO_IO_HANDLE_NONE otherwise.
+ */
audio_io_handle_t getIoForSession(audio_session_t sessionId,
const effect_uuid_t *effectType = nullptr);
-
+ bool hasOrphansForSession(audio_session_t sessionId);
+ EffectDescriptorCollection getOrphanEffectsForSession(audio_session_t sessionId) const;
void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
private:
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 3f9c8b0..216d2eb 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -18,9 +18,15 @@
//#define LOG_NDEBUG 0
#include <android-base/stringprintf.h>
+
+#include "AudioInputDescriptor.h"
#include "EffectDescriptor.h"
#include <utils/String8.h>
+#include <AudioPolicyInterface.h>
+#include "AudioPolicyMix.h"
+#include "HwModule.h"
+
namespace android {
void EffectDescriptor::dump(String8 *dst, int spaces) const
@@ -175,30 +181,57 @@
return MAX_EFFECTS_MEMORY;
}
-void EffectDescriptorCollection::moveEffects(audio_session_t session,
- audio_io_handle_t srcOutput,
- audio_io_handle_t dstOutput)
+void EffectDescriptorCollection::moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
+ audio_io_handle_t dstIo,
+ AudioPolicyClientInterface *clientInterface)
{
- ALOGV("%s session %d srcOutput %d dstOutput %d", __func__, session, srcOutput, dstOutput);
+ ALOGV("%s session %d srcIo %d dstIo %d", __func__, sessionId, srcIo, dstIo);
for (size_t i = 0; i < size(); i++) {
sp<EffectDescriptor> effect = valueAt(i);
- if (effect->mSession == session && effect->mIo == srcOutput) {
- effect->mIo = dstOutput;
+ if (effect->mSession == sessionId && effect->mIo == srcIo) {
+ effect->mIo = dstIo;
+ // Backup enable state before any updatePolicyState call
+ effect->mIsOrphan = (dstIo == AUDIO_IO_HANDLE_NONE);
+ }
+ }
+ clientInterface->moveEffects(sessionId, srcIo, dstIo);
+}
+
+void EffectDescriptorCollection::moveEffects(const std::vector<int>& ids, audio_io_handle_t dstIo)
+{
+ ALOGV("%s num effects %zu, first ID %d, dstIo %d",
+ __func__, ids.size(), ids.size() ? ids[0] : 0, dstIo);
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (std::find(begin(ids), end(ids), effect->mId) != end(ids)) {
+ effect->mIo = dstIo;
+ effect->mIsOrphan = (dstIo == AUDIO_IO_HANDLE_NONE);
}
}
}
-void EffectDescriptorCollection::moveEffects(const std::vector<int>& ids,
- audio_io_handle_t dstOutput)
+bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId)
{
- ALOGV("%s num effects %zu, first ID %d, dstOutput %d",
- __func__, ids.size(), ids.size() ? ids[0] : 0, dstOutput);
- for (size_t i = 0; i < size(); i++) {
+ for (size_t i = 0; i < size(); ++i) {
sp<EffectDescriptor> effect = valueAt(i);
- if (std::find(begin(ids), end(ids), effect->mId) != end(ids)) {
- effect->mIo = dstOutput;
+ if (effect->mSession == sessionId && effect->mIsOrphan) {
+ return true;
}
}
+ return false;
+}
+
+EffectDescriptorCollection EffectDescriptorCollection::getOrphanEffectsForSession(
+ audio_session_t sessionId) const
+{
+ EffectDescriptorCollection effects;
+ for (size_t i = 0; i < size(); i++) {
+ sp<EffectDescriptor> effect = valueAt(i);
+ if (effect->mSession == sessionId && effect->mIsOrphan) {
+ effects.add(keyAt(i), effect);
+ }
+ }
+ return effects;
}
audio_io_handle_t EffectDescriptorCollection::getIoForSession(audio_session_t sessionId,
@@ -214,6 +247,67 @@
return AUDIO_IO_HANDLE_NONE;
}
+void EffectDescriptorCollection::moveEffectsForIo(audio_session_t session,
+ audio_io_handle_t dstIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ // No src io: try to find from effect session the src Io to move from
+ audio_io_handle_t srcIo = getIoForSession(session);
+ if (hasOrphansForSession(session) || (srcIo != AUDIO_IO_HANDLE_NONE && srcIo != dstIo)) {
+ moveEffects(session, srcIo, dstIo, inputs, clientInterface);
+ }
+}
+
+void EffectDescriptorCollection::moveEffects(audio_session_t session,
+ audio_io_handle_t srcIo, audio_io_handle_t dstIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ if ((srcIo != AUDIO_IO_HANDLE_NONE && srcIo == dstIo)
+ || (srcIo == AUDIO_IO_HANDLE_NONE && !hasOrphansForSession(session))) {
+ return;
+ }
+ // Either we may find orphan effects for given session or effects for this session might have
+ // been assigned first to another input (it may happen when an input is released or recreated
+ // after client sets its preferred device)
+ EffectDescriptorCollection effectsToMove;
+ if (srcIo == AUDIO_IO_HANDLE_NONE) {
+ ALOGV("%s: restoring effects for session %d from orphan park to io=%d", __func__,
+ session, dstIo);
+ effectsToMove = getOrphanEffectsForSession(session);
+ } else {
+ ALOGV("%s: moving effects for session %d from io=%d to io=%d", __func__,
+ session, srcIo, dstIo);
+ sp<AudioInputDescriptor> previousInputDesc = inputs->valueFor(srcIo);
+ effectsToMove = getEffectsForIo(srcIo);
+ for (size_t i = 0; i < effectsToMove.size(); ++i) {
+ sp<EffectDescriptor> effect = effectsToMove.valueAt(i);
+ effect->mEnabledWhenMoved = effect->mEnabled;
+ previousInputDesc->trackEffectEnabled(effect, false);
+ }
+ }
+ moveEffects(session, srcIo, dstIo, clientInterface);
+
+ if (dstIo != AUDIO_IO_HANDLE_NONE) {
+ sp<AudioInputDescriptor> inputDesc = inputs->valueFor(dstIo);
+ for (size_t i = 0; i < effectsToMove.size(); ++i) {
+ sp<EffectDescriptor> effect = effectsToMove.valueAt(i);
+ inputDesc->trackEffectEnabled(effect, effect->mEnabledWhenMoved);
+ }
+ }
+}
+
+void EffectDescriptorCollection::putOrphanEffects(audio_session_t session,
+ audio_io_handle_t srcIo, const AudioInputCollection *inputs,
+ AudioPolicyClientInterface *clientInterface)
+{
+ if (getIoForSession(session) != srcIo) {
+ // Effect session not held by this client io handle
+ return;
+ }
+ ALOGV("%s: park effects for session %d and io=%d to orphans", __func__, session, srcIo);
+ moveEffects(session, srcIo, AUDIO_IO_HANDLE_NONE, inputs, clientInterface);
+}
+
EffectDescriptorCollection EffectDescriptorCollection::getEffectsForIo(audio_io_handle_t io) const
{
EffectDescriptorCollection effects;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0e971b0..92b58ec 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2650,6 +2650,7 @@
sp<AudioPolicyMix> policyMix;
sp<DeviceDescriptor> device;
sp<AudioInputDescriptor> inputDesc;
+ sp<AudioInputDescriptor> previousInputDesc;
sp<RecordClientDescriptor> clientDesc;
audio_port_handle_t requestedDeviceId = *selectedDeviceId;
uid_t uid = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_uid_t(attributionSource.uid));
@@ -2806,6 +2807,8 @@
requestedDeviceId, attributes.source, flags,
isSoundTrigger);
inputDesc = mInputs.valueFor(*input);
+ // Move (if found) effect for the client session to its input
+ mEffects.moveEffectsForIo(session, *input, &mInputs, mpClientInterface);
inputDesc->addClient(clientDesc);
ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d",
@@ -3115,7 +3118,7 @@
ALOGV("%s %d", __FUNCTION__, input);
inputDesc->removeClient(portId);
-
+ mEffects.putOrphanEffects(client->session(), input, &mInputs, mpClientInterface);
if (inputDesc->getClientCount() > 0) {
ALOGV("%s(%d) %zu clients remaining", __func__, portId, inputDesc->getClientCount());
return;
@@ -3477,8 +3480,8 @@
}
if (output != mMusicEffectOutput) {
- mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output);
- mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output);
+ mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output,
+ mpClientInterface);
mMusicEffectOutput = output;
}