AudioEffect: prevent adding effect for unknown session on first io
Bug: 309578734
Test: add an effect chain on a music ouput (first) and enable.
Add another chain on another output.
Ensure chain on music output is not suspended.
Adding a chain on a different output may suspend chain on default output
First output is assigned when an effect is created, then may be moved once the
session is associated to its output.
It leads to suspend the chain of first output.
This CL fixes this bug by adding effect in orphan chains until track is
created.
Test: atest CtsMediaAudioTestCases
Change-Id: I2ce880dea862df1b29a67893d60a9c98c4c4eb46
Merged-In: I2ce880dea862df1b29a67893d60a9c98c4c4eb46
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a23c3ba..dec640d 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1082,6 +1082,7 @@
client = registerPid(clientPid);
IAfPlaybackThread* effectThread = nullptr;
+ sp<IAfEffectChain> effectChain = nullptr;
// check if an effect chain with the same session ID is present on another
// output thread and move it here.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
@@ -1094,6 +1095,10 @@
}
}
}
+ // Check if an orphan effect chain exists for this session
+ if (effectThread == nullptr) {
+ effectChain = getOrphanEffectChain_l(sessionId);
+ }
ALOGV("createTrack() sessionId: %d", sessionId);
output.sampleRate = input.config.sample_rate;
@@ -1138,6 +1143,13 @@
effectIds = thread->getEffectIds_l(sessionId);
}
}
+ if (effectChain != nullptr) {
+ if (moveEffectChain_ll(sessionId, nullptr, thread, effectChain.get())
+ == NO_ERROR) {
+ effectThreadId = thread->id();
+ effectIds = thread->getEffectIds_l(sessionId);
+ }
+ }
}
// Look for sync events awaiting for a session to be used.
@@ -4280,8 +4292,9 @@
// before creating the AudioEffect or the io handle must be specified.
//
// Detect if the effect is created after an AudioRecord is destroyed.
- if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) &&
- getOrphanEffectChain_l(sessionId).get() != nullptr) {
+ if (sessionId != AUDIO_SESSION_OUTPUT_MIX
+ && ((descOut.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)
+ && getOrphanEffectChain_l(sessionId).get() != nullptr) {
ALOGE("%s: effect %s with no specified io handle is denied because the AudioRecord"
" for session %d no longer exists",
__func__, descOut.name, sessionId);
@@ -4292,11 +4305,27 @@
// Legacy handling of creating an effect on an expired or made-up
// session id. We think that it is a Playback effect.
//
- // If no output thread contains the requested session ID, default to
- // first output. The effect chain will be moved to the correct output
- // thread when a track with the same session ID is created
- if (io == AUDIO_IO_HANDLE_NONE && mPlaybackThreads.size() > 0) {
- io = mPlaybackThreads.keyAt(0);
+ // If no output thread contains the requested session ID, park the effect to
+ // the orphan chains. The effect chain will be moved to the correct output
+ // thread when a track with the same session ID is created.
+ if (io == AUDIO_IO_HANDLE_NONE) {
+ if (probe) {
+ // In probe mode, as no compatible thread found, exit with error.
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ ALOGV("%s() got io %d for effect %s", __func__, io, descOut.name);
+ sp<Client> client = registerPid(currentPid);
+ bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
+ handle = createOrphanEffect_l(client, effectClient, priority, sessionId,
+ &descOut, &enabledOut, &lStatus, pinned,
+ request.notifyFramesProcessed);
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ // remove local strong reference to Client with clientMutex() held
+ audio_utils::lock_guard _cl(clientMutex());
+ client.clear();
+ }
+ goto Register;
}
ALOGV("createEffect() got io %d for effect %s", io, descOut.name);
} else if (checkPlaybackThread_l(io) != nullptr
@@ -4414,6 +4443,78 @@
return lStatus;
}
+sp<IAfEffectHandle> AudioFlinger::createOrphanEffect_l(
+ const sp<Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ audio_session_t sessionId,
+ effect_descriptor_t *desc,
+ int *enabled,
+ status_t *status,
+ bool pinned,
+ bool notifyFramesProcessed)
+{
+ ALOGV("%s effectClient %p, priority %d, sessionId %d, factory %p",
+ __func__, effectClient.get(), priority, sessionId, mEffectsFactoryHal.get());
+
+ // Check if an orphan effect chain exists for this session or create new chain for this session
+ sp<IAfEffectModule> effect;
+ sp<IAfEffectChain> chain = getOrphanEffectChain_l(sessionId);
+ bool chainCreated = false;
+ if (chain == nullptr) {
+ chain = IAfEffectChain::create(/* ThreadBase= */ nullptr, sessionId, this);
+ chainCreated = true;
+ } else {
+ effect = chain->getEffectFromDesc(desc);
+ }
+ bool effectCreated = false;
+ if (effect == nullptr) {
+ audio_unique_id_t effectId = nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
+ // create a new effect module if none present in the chain
+ status_t llStatus =
+ chain->createEffect(effect, desc, effectId, sessionId, pinned);
+ if (llStatus != NO_ERROR) {
+ *status = llStatus;
+ return nullptr;
+ }
+ effect->setMode(getMode());
+
+ if (effect->isHapticGenerator()) {
+ // TODO(b/184194057): Use the vibrator information from the vibrator that will be used
+ // for the HapticGenerator.
+ const std::optional<media::AudioVibratorInfo> defaultVibratorInfo =
+ std::move(getDefaultVibratorInfo_l());
+ if (defaultVibratorInfo) {
+ // Only set the vibrator info when it is a valid one.
+ audio_utils::lock_guard _cl(chain->mutex());
+ effect->setVibratorInfo_l(*defaultVibratorInfo);
+ }
+ }
+ effectCreated = true;
+ }
+ // create effect handle and connect it to effect module
+ sp<IAfEffectHandle> handle =
+ IAfEffectHandle::create(effect, client, effectClient, priority, notifyFramesProcessed);
+ status_t lStatus = handle->initCheck();
+ if (lStatus == OK) {
+ lStatus = effect->addHandle(handle.get());
+ }
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ if (effectCreated) {
+ chain->removeEffect(effect);
+ }
+ } else {
+ if (enabled != NULL) {
+ *enabled = (int)effect->isEnabled();
+ }
+ if (chainCreated) {
+ putOrphanEffectChain_l(chain);
+ }
+ }
+ *status = lStatus;
+ return handle;
+}
+
status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t srcIo,
audio_io_handle_t dstIo)
NO_THREAD_SAFETY_ANALYSIS
@@ -4545,7 +4646,7 @@
if (srcThread != nullptr) {
srcThread->removeEffect_l(effect);
} else {
- chain->removeEffect_l(effect);
+ chain->removeEffect(effect);
}
removed.add(effect);
status = dstThread->addEffect_ll(effect);
@@ -4788,7 +4889,7 @@
ALOGV("updateOrphanEffectChains session %d index %zd", session, index);
if (index >= 0) {
sp<IAfEffectChain> chain = mOrphanEffectChains.valueAt(index);
- if (chain->removeEffect_l(effect, true) == 0) {
+ if (chain->removeEffect(effect, true) == 0) {
ALOGV("updateOrphanEffectChains removing effect chain at index %zd", index);
mOrphanEffectChains.removeItemsAt(index);
}