AudioFlinger: Keep track of music effect thread when primary output is unavailable
Bug: 307216402
Test: make
Audio Policy Manager may temporary run without primary output.
If music effects are instantiated, APM will lose track of music
output (may point to invalid io handle of primary output).
AF will save these effects on first playback thread.
Thus, these music effects become impossible to be moved back to
music playback thread.
This CL fixes this by:
-using chain parking mecanism in audio flinger when output
hosting session mix is closed
-fixing session mix creation if APM has temporarily no output:
in this case, AudioFlinger will use the first available output to host
output mix effect
-enhancing moveEffect function to check in the orphan chains if
the source output is not found
-enhancing moveEffect function to check among other outputs
when source is not found to find the output mix session.
Signed-off-by: Francois Gaffie <francois.gaffie@renault.com>
Change-Id: I7690aab62d6dabc6351e7b283810bdcaf92bb3de
Merged-In: I7690aab62d6dabc6351e7b283810bdcaf92bb3de
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 725e5a6..01f55dd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3059,6 +3059,25 @@
mPlaybackThreads.removeItem(output);
+ // Save AUDIO_SESSION_OUTPUT_MIX effect to orphan chains
+ // Output Mix Effect session is used to manage Music Effect by AudioPolicy Manager.
+ // It exists across all playback threads.
+ if (playbackThread->type() == IAfThreadBase::MIXER
+ || playbackThread->type() == IAfThreadBase::OFFLOAD
+ || playbackThread->type() == IAfThreadBase::SPATIALIZER) {
+ sp<IAfEffectChain> mixChain;
+ {
+ audio_utils::scoped_lock sl(playbackThread->mutex());
+ mixChain = playbackThread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (mixChain != nullptr) {
+ ALOGW("%s() output %d moving mix session to orphans", __func__, output);
+ playbackThread->removeEffectChain_l(mixChain);
+ }
+ }
+ if (mixChain != nullptr) {
+ putOrphanEffectChain_l(mixChain);
+ }
+ }
// save all effects to the default thread
if (mPlaybackThreads.size()) {
IAfPlaybackThread* const dstThread =
@@ -4203,7 +4222,8 @@
// before creating the AudioEffect or the io handle must be specified.
//
// Detect if the effect is created after an AudioRecord is destroyed.
- if (getOrphanEffectChain_l(sessionId).get() != nullptr) {
+ if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) &&
+ 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);
@@ -4259,7 +4279,8 @@
goto Exit;
}
}
- } else {
+ }
+ if (thread->type() == IAfThreadBase::RECORD || sessionId == AUDIO_SESSION_OUTPUT_MIX) {
// Check if one effect chain was awaiting for an effect to be created on this
// session and used it instead of creating a new one.
sp<IAfEffectChain> chain = getOrphanEffectChain_l(sessionId);
@@ -4363,17 +4384,39 @@
}
return ret;
}
- IAfPlaybackThread* const srcThread = checkPlaybackThread_l(srcIo);
- if (srcThread == nullptr) {
- ALOGW("%s() bad srcIo %d", __func__, srcIo);
- return BAD_VALUE;
- }
- IAfPlaybackThread* const dstThread = checkPlaybackThread_l(dstIo);
+
+ IAfPlaybackThread* dstThread = checkPlaybackThread_l(dstIo);
if (dstThread == nullptr) {
ALOGW("%s() bad dstIo %d", __func__, dstIo);
return BAD_VALUE;
}
+ IAfPlaybackThread* srcThread = checkPlaybackThread_l(srcIo);
+ sp<IAfEffectChain> orphanChain = getOrphanEffectChain_l(sessionId);
+ if (srcThread == nullptr && orphanChain == nullptr && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ ALOGW("%s() AUDIO_SESSION_OUTPUT_MIX not found in orphans, checking other mix", __func__);
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ const sp<IAfPlaybackThread> pt = mPlaybackThreads.valueAt(i);
+ const uint32_t sessionType = pt->hasAudioSession(AUDIO_SESSION_OUTPUT_MIX);
+ if ((pt->type() == IAfThreadBase::MIXER || pt->type() == IAfThreadBase::OFFLOAD) &&
+ ((sessionType & IAfThreadBase::EFFECT_SESSION) != 0)) {
+ srcThread = pt.get();
+ ALOGW("%s() found srcOutput %d hosting AUDIO_SESSION_OUTPUT_MIX", __func__,
+ pt->id());
+ break;
+ }
+ }
+ }
+ if (srcThread == nullptr && orphanChain == nullptr) {
+ ALOGW("moveEffects() bad srcIo %d", srcIo);
+ return BAD_VALUE;
+ }
+ // dstThread pointer validity has already been checked
+ if (orphanChain != nullptr) {
+ audio_utils::scoped_lock _ll(dstThread->mutex());
+ return moveEffectChain_ll(sessionId, nullptr, dstThread, orphanChain.get());
+ }
+ // srcThread pointer validity has already been checked
audio_utils::scoped_lock _ll(dstThread->mutex(), srcThread->mutex());
return moveEffectChain_ll(sessionId, srcThread, dstThread);
}
@@ -4399,12 +4442,17 @@
// moveEffectChain_ll must be called with the AudioFlinger::mutex()
// and both srcThread and dstThread mutex()s held
status_t AudioFlinger::moveEffectChain_ll(audio_session_t sessionId,
- IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread)
+ IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread,
+ IAfEffectChain* srcChain)
{
- ALOGV("%s: session %d from thread %p to thread %p",
- __func__, sessionId, srcThread, dstThread);
+ ALOGV("%s: session %d from thread %p to thread %p %s",
+ __func__, sessionId, srcThread, dstThread,
+ (srcChain != nullptr ? "from specific chain" : ""));
+ ALOG_ASSERT((srcThread != nullptr) != (srcChain != nullptr),
+ "no source provided for source chain");
- sp<IAfEffectChain> chain = srcThread->getEffectChain_l(sessionId);
+ sp<IAfEffectChain> chain =
+ srcChain != nullptr ? srcChain : srcThread->getEffectChain_l(sessionId);
if (chain == 0) {
ALOGW("%s: effect chain for session %d not on source thread %p",
__func__, sessionId, srcThread);
@@ -4424,8 +4472,9 @@
// otherwise unnecessary as removeEffect_l() will remove the chain when last effect is
// removed.
// TODO(b/216875016): consider holding the effect chain locks for the duration of the move.
- srcThread->removeEffectChain_l(chain);
-
+ if (srcThread != nullptr) {
+ srcThread->removeEffectChain_l(chain);
+ }
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
sp<IAfEffectChain> dstChain;
@@ -4435,7 +4484,11 @@
// process effects one by one.
for (sp<IAfEffectModule> effect = chain->getEffectFromId_l(0); effect != nullptr;
effect = chain->getEffectFromId_l(0)) {
- srcThread->removeEffect_l(effect);
+ if (srcThread != nullptr) {
+ srcThread->removeEffect_l(effect);
+ } else {
+ chain->removeEffect_l(effect);
+ }
removed.add(effect);
status = dstThread->addEffect_ll(effect);
if (status != NO_ERROR) {
@@ -4463,7 +4516,7 @@
for (const auto& effect : removed) {
dstThread->removeEffect_l(effect); // Note: Depending on error location, the last
// effect may not have been placed on dstThread.
- if (srcThread->addEffect_ll(effect) == NO_ERROR) {
+ if (srcThread != nullptr && srcThread->addEffect_ll(effect) == NO_ERROR) {
++restored;
if (dstChain == nullptr) {
dstChain = effect->getCallback()->chain().promote();
@@ -4494,15 +4547,19 @@
if (errorString.empty()) {
errorString = StringPrintf("%s: failed status %d", __func__, status);
}
- ALOGW("%s: %s unsuccessful move of session %d from srcThread %p to dstThread %p "
+ ALOGW("%s: %s unsuccessful move of session %d from %s %p to dstThread %p "
"(%zu effects removed from srcThread, %zu effects restored to srcThread, "
"%zu effects started)",
- __func__, errorString.c_str(), sessionId, srcThread, dstThread,
+ __func__, errorString.c_str(), sessionId,
+ (srcThread != nullptr ? "srcThread" : "srcChain"),
+ (srcThread != nullptr ? (void*) srcThread : (void*) srcChain), dstThread,
removed.size(), restored, started);
} else {
- ALOGD("%s: successful move of session %d from srcThread %p to dstThread %p "
+ ALOGD("%s: successful move of session %d from %s %p to dstThread %p "
"(%zu effects moved, %zu effects started)",
- __func__, sessionId, srcThread, dstThread, removed.size(), started);
+ __func__, sessionId, (srcThread != nullptr ? "srcThread" : "srcChain"),
+ (srcThread != nullptr ? (void*) srcThread : (void*) srcChain), dstThread,
+ removed.size(), started);
}
return status;
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 0f75d6e..7c26f72 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -373,7 +373,8 @@
EXCLUDES_AudioFlinger_Mutex;
status_t moveEffectChain_ll(audio_session_t sessionId,
- IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread) final
+ IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread,
+ IAfEffectChain* srcChain = nullptr) final
REQUIRES(mutex(), audio_utils::ThreadBase_Mutex);
// This is a helper that is called during incoming binder calls.
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b270813..307a8e2 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2487,6 +2487,7 @@
size_t size = mEffects.size();
uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
+ const bool hasThreadAttached = mEffectCallback->hasThreadAttached();
for (size_t i = 0; i < size; i++) {
if (effect == mEffects[i]) {
// calling stop here will remove pre-processing effect from the audio HAL.
@@ -2499,8 +2500,8 @@
if (release) {
mEffects[i]->release_l();
}
-
- if (type != EFFECT_FLAG_TYPE_AUXILIARY) {
+ // Skip operation when no thread attached (could lead to sigfpe as framecount is 0...)
+ if (hasThreadAttached && type != EFFECT_FLAG_TYPE_AUXILIARY) {
if (i == size - 1 && i != 0) {
mEffects[i - 1]->configure_l();
mEffects[i - 1]->setOutBuffer(mOutBuffer);
@@ -2512,7 +2513,7 @@
// make sure the input buffer configuration for the new first effect in the chain
// is updated if needed (can switch from HAL channel mask to mixer channel mask)
if (type != EFFECT_FLAG_TYPE_AUXILIARY // TODO(b/284522658) breaks for aux FX, why?
- && i == 0 && size > 1) {
+ && hasThreadAttached && i == 0 && size > 1) {
mEffects[0]->configure_l();
mEffects[0]->setInBuffer(mInBuffer);
mEffects[0]->updateAccessMode_l(); // reconfig if needed.
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 46c44a6..5e527d3 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -616,7 +616,9 @@
mThreadType = thread->type();
mAfThreadCallback = thread->afThreadCallback();
}
-
+ bool hasThreadAttached() const {
+ return thread().promote() != nullptr;
+ }
private:
const wp<IAfEffectChain> mChain;
mediautils::atomic_wp<IAfThreadBase> mThread;
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index d701288..4b6ab89 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -95,7 +95,8 @@
virtual bool updateOrphanEffectChains(const sp<IAfEffectModule>& effect)
EXCLUDES_AudioFlinger_Mutex = 0;
virtual status_t moveEffectChain_ll(audio_session_t sessionId,
- IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread)
+ IAfPlaybackThread* srcThread, IAfPlaybackThread* dstThread,
+ IAfEffectChain* srcChain = nullptr)
REQUIRES(mutex(), audio_utils::ThreadBase_Mutex) = 0;
virtual void requestLogMerge() = 0;