Do not invalidate stream when the secondary outputs are changed.
When a dynamic policy is registered, the secondary outputs may be
changed. Instead of tearing down the tracks, only tracks whose secondary
outputs are changed will be updated with the new secondary outputs.
Bug: 181582467
Bug: 174123397
Test: atest AudioPlaybackCaptureTest audiopolicy_tests
Test: repo steps in the bug
Change-Id: I9a47a0a4b37ad3f4a1d554dd726ebffb27325141
(cherry picked from commit 10a03f1713e29eadfa862087e504f9e14964387e)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3562b00..00f423c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -312,6 +312,27 @@
return NO_ERROR;
}
+status_t AudioFlinger::updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs) {
+ Mutex::Autolock _l(mLock);
+ for (const auto& [trackId, secondaryOutputs] : trackSecondaryOutputs) {
+ size_t i = 0;
+ for (; i < mPlaybackThreads.size(); ++i) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ Mutex::Autolock _tl(thread->mLock);
+ sp<PlaybackThread::Track> track = thread->getTrackById_l(trackId);
+ if (track != nullptr) {
+ ALOGD("%s trackId: %u", __func__, trackId);
+ updateSecondaryOutputsForTrack_l(track.get(), thread, secondaryOutputs);
+ break;
+ }
+ }
+ ALOGW_IF(i >= mPlaybackThreads.size(),
+ "%s cannot find track with id %u", __func__, trackId);
+ }
+ return NO_ERROR;
+}
+
// getDefaultVibratorInfo_l must be called with AudioFlinger lock held.
const media::AudioVibratorInfo* AudioFlinger::getDefaultVibratorInfo_l() {
if (mAudioVibratorInfos.empty()) {
@@ -944,88 +965,7 @@
// Connect secondary outputs. Failure on a secondary output must not imped the primary
// Any secondary output setup failure will lead to a desync between the AP and AF until
// the track is destroyed.
- TeePatches teePatches;
- for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
- PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
- if (secondaryThread == NULL) {
- ALOGE("no playback thread found for secondary output %d", output.outputId);
- continue;
- }
-
- size_t sourceFrameCount = thread->frameCount() * output.sampleRate
- / thread->sampleRate();
- size_t sinkFrameCount = secondaryThread->frameCount() * output.sampleRate
- / secondaryThread->sampleRate();
- // If the secondary output has just been opened, the first secondaryThread write
- // will not block as it will fill the empty startup buffer of the HAL,
- // so a second sink buffer needs to be ready for the immediate next blocking write.
- // Additionally, have a margin of one main thread buffer as the scheduling jitter
- // can reorder the writes (eg if thread A&B have the same write intervale,
- // the scheduler could schedule AB...BA)
- size_t frameCountToBeReady = 2 * sinkFrameCount + sourceFrameCount;
- // Total secondary output buffer must be at least as the read frames plus
- // the margin of a few buffers on both sides in case the
- // threads scheduling has some jitter.
- // That value should not impact latency as the secondary track is started before
- // its buffer is full, see frameCountToBeReady.
- size_t frameCount = frameCountToBeReady + 2 * (sourceFrameCount + sinkFrameCount);
- // The frameCount should also not be smaller than the secondary thread min frame
- // count
- size_t minFrameCount = AudioSystem::calculateMinFrameCount(
- [&] { Mutex::Autolock _l(secondaryThread->mLock);
- return secondaryThread->latency_l(); }(),
- secondaryThread->mNormalFrameCount,
- secondaryThread->mSampleRate,
- output.sampleRate,
- input.speed);
- frameCount = std::max(frameCount, minFrameCount);
-
- using namespace std::chrono_literals;
- auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask);
- sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
- output.sampleRate,
- inChannelMask,
- input.config.format,
- frameCount,
- NULL /* buffer */,
- (size_t)0 /* bufferSize */,
- AUDIO_INPUT_FLAG_DIRECT,
- 0ns /* timeout */);
- status_t status = patchRecord->initCheck();
- if (status != NO_ERROR) {
- ALOGE("Secondary output patchRecord init failed: %d", status);
- continue;
- }
-
- // TODO: We could check compatibility of the secondaryThread with the PatchTrack
- // for fast usage: thread has fast mixer, sample rate matches, etc.;
- // for now, we exclude fast tracks by removing the Fast flag.
- const audio_output_flags_t outputFlags =
- (audio_output_flags_t)(output.flags & ~AUDIO_OUTPUT_FLAG_FAST);
- sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
- streamType,
- output.sampleRate,
- input.config.channel_mask,
- input.config.format,
- frameCount,
- patchRecord->buffer(),
- patchRecord->bufferSize(),
- outputFlags,
- 0ns /* timeout */,
- frameCountToBeReady);
- status = patchTrack->initCheck();
- if (status != NO_ERROR) {
- ALOGE("Secondary output patchTrack init failed: %d", status);
- continue;
- }
- teePatches.push_back({patchRecord, patchTrack});
- secondaryThread->addPatchTrack(patchTrack);
- // In case the downstream patchTrack on the secondaryThread temporarily outlives
- // our created track, ensure the corresponding patchRecord is still alive.
- patchTrack->setPeerProxy(patchRecord, true /* holdReference */);
- patchRecord->setPeerProxy(patchTrack, false /* holdReference */);
- }
- track->setTeePatches(std::move(teePatches));
+ updateSecondaryOutputsForTrack_l(track.get(), thread, secondaryOutputs);
}
// move effect chain to this output thread if an effect on same session was waiting
@@ -3441,6 +3381,94 @@
return nullptr;
}
+void AudioFlinger::updateSecondaryOutputsForTrack_l(
+ PlaybackThread::Track* track,
+ PlaybackThread* thread,
+ const std::vector<audio_io_handle_t> &secondaryOutputs) const {
+ TeePatches teePatches;
+ for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
+ PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
+ if (secondaryThread == nullptr) {
+ ALOGE("no playback thread found for secondary output %d", thread->id());
+ continue;
+ }
+
+ size_t sourceFrameCount = thread->frameCount() * track->sampleRate()
+ / thread->sampleRate();
+ size_t sinkFrameCount = secondaryThread->frameCount() * track->sampleRate()
+ / secondaryThread->sampleRate();
+ // If the secondary output has just been opened, the first secondaryThread write
+ // will not block as it will fill the empty startup buffer of the HAL,
+ // so a second sink buffer needs to be ready for the immediate next blocking write.
+ // Additionally, have a margin of one main thread buffer as the scheduling jitter
+ // can reorder the writes (eg if thread A&B have the same write intervale,
+ // the scheduler could schedule AB...BA)
+ size_t frameCountToBeReady = 2 * sinkFrameCount + sourceFrameCount;
+ // Total secondary output buffer must be at least as the read frames plus
+ // the margin of a few buffers on both sides in case the
+ // threads scheduling has some jitter.
+ // That value should not impact latency as the secondary track is started before
+ // its buffer is full, see frameCountToBeReady.
+ size_t frameCount = frameCountToBeReady + 2 * (sourceFrameCount + sinkFrameCount);
+ // The frameCount should also not be smaller than the secondary thread min frame
+ // count
+ size_t minFrameCount = AudioSystem::calculateMinFrameCount(
+ [&] { Mutex::Autolock _l(secondaryThread->mLock);
+ return secondaryThread->latency_l(); }(),
+ secondaryThread->mNormalFrameCount,
+ secondaryThread->mSampleRate,
+ track->sampleRate(),
+ track->getSpeed());
+ frameCount = std::max(frameCount, minFrameCount);
+
+ using namespace std::chrono_literals;
+ auto inChannelMask = audio_channel_mask_out_to_in(track->channelMask());
+ sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
+ track->sampleRate(),
+ inChannelMask,
+ track->format(),
+ frameCount,
+ nullptr /* buffer */,
+ (size_t)0 /* bufferSize */,
+ AUDIO_INPUT_FLAG_DIRECT,
+ 0ns /* timeout */);
+ status_t status = patchRecord->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("Secondary output patchRecord init failed: %d", status);
+ continue;
+ }
+
+ // TODO: We could check compatibility of the secondaryThread with the PatchTrack
+ // for fast usage: thread has fast mixer, sample rate matches, etc.;
+ // for now, we exclude fast tracks by removing the Fast flag.
+ const audio_output_flags_t outputFlags =
+ (audio_output_flags_t)(track->getOutputFlags() & ~AUDIO_OUTPUT_FLAG_FAST);
+ sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
+ track->streamType(),
+ track->sampleRate(),
+ track->channelMask(),
+ track->format(),
+ frameCount,
+ patchRecord->buffer(),
+ patchRecord->bufferSize(),
+ outputFlags,
+ 0ns /* timeout */,
+ frameCountToBeReady);
+ status = patchTrack->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("Secondary output patchTrack init failed: %d", status);
+ continue;
+ }
+ teePatches.push_back({patchRecord, patchTrack});
+ secondaryThread->addPatchTrack(patchTrack);
+ // In case the downstream patchTrack on the secondaryThread temporarily outlives
+ // our created track, ensure the corresponding patchRecord is still alive.
+ patchTrack->setPeerProxy(patchRecord, true /* holdReference */);
+ patchRecord->setPeerProxy(patchTrack, false /* holdReference */);
+ }
+ track->setTeePatches(std::move(teePatches));
+}
+
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
audio_session_t triggerSession,
audio_session_t listenerSession,
@@ -4170,7 +4198,8 @@
case TransactionCode::SET_LOW_RAM_DEVICE:
case TransactionCode::SYSTEM_READY:
case TransactionCode::SET_AUDIO_HAL_PIDS:
- case TransactionCode::SET_VIBRATOR_INFOS: {
+ case TransactionCode::SET_VIBRATOR_INFOS:
+ case TransactionCode::UPDATE_SECONDARY_OUTPUTS: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 4b03d10..b12f52e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -272,6 +272,9 @@
virtual status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
+ virtual status_t updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs);
+
status_t onTransactWrapper(TransactionCode code, const Parcel& data, uint32_t flags,
const std::function<status_t()>& delegate) override;
@@ -775,6 +778,11 @@
ThreadBase *hapticPlaybackThread_l() const;
+ void updateSecondaryOutputsForTrack_l(
+ PlaybackThread::Track* track,
+ PlaybackThread* thread,
+ const std::vector<audio_io_handle_t>& secondaryOutputs) const;
+
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 0af4c7b..30a2432 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -81,7 +81,8 @@
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
/** default behaviour is to start when there are as many frames
* ready as possible (aka. Buffer is full). */
- size_t frameCountToBeReady = SIZE_MAX);
+ size_t frameCountToBeReady = SIZE_MAX,
+ float speed = 1.0f);
virtual ~Track();
virtual status_t initCheck() const;
@@ -183,6 +184,9 @@
mAudioTrackServerProxy->getUnderrunFrames());
}
}
+
+ audio_output_flags_t getOutputFlags() const { return mFlags; }
+ float getSpeed() const { return mSpeed; }
protected:
// for numerous
friend class PlaybackThread;
@@ -311,6 +315,7 @@
bool mPauseHwPending = false; // direct/offload track request for thread pause
audio_output_flags_t mFlags;
TeePatches mTeePatches;
+ const float mSpeed;
}; // end of Track
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d878611..25a19a2 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2425,7 +2425,7 @@
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, creatorPid, identity, trackFlags, TrackBase::TYPE_DEFAULT,
- portId, SIZE_MAX /*frameCountToBeReady*/);
+ portId, SIZE_MAX /*frameCountToBeReady*/, speed);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
@@ -3321,6 +3321,17 @@
invalidateTracks_l(streamType);
}
+// getTrackById_l must be called with holding thread lock
+AudioFlinger::PlaybackThread::Track* AudioFlinger::PlaybackThread::getTrackById_l(
+ audio_port_handle_t trackPortId) {
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ if (mTracks[i]->portId() == trackPortId) {
+ return mTracks[i].get();
+ }
+ }
+ return nullptr;
+}
+
status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
{
audio_session_t session = chain->sessionId();
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 17acb16..d4fb995 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1013,6 +1013,8 @@
mDownStreamPatch = *patch;
}
+ PlaybackThread::Track* getTrackById_l(audio_port_handle_t trackId);
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8be7c86..09e5ec5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -624,7 +624,8 @@
audio_output_flags_t flags,
track_type type,
audio_port_handle_t portId,
- size_t frameCountToBeReady)
+ size_t frameCountToBeReady,
+ float speed)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -658,7 +659,8 @@
mFinalVolume(0.f),
mResumeToStopping(false),
mFlushHwPending(false),
- mFlags(flags)
+ mFlags(flags),
+ mSpeed(speed)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -1404,6 +1406,10 @@
void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
mTeePatches = std::move(teePatches);
+ if (mState == TrackBase::ACTIVE || mState == TrackBase::RESUMING ||
+ mState == TrackBase::STOPPING_1) {
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); });
+ }
}
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 5f052a5..a904321 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOPOLICY_INTERFACE_H
#define ANDROID_AUDIOPOLICY_INTERFACE_H
+#include <media/AudioCommonTypes.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioSystem.h>
#include <media/AudioPolicy.h>
@@ -453,6 +454,9 @@
virtual void setSoundTriggerCaptureState(bool active) = 0;
virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
+
+ virtual status_t updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs) = 0;
};
// These are the signatures of createAudioPolicyManager/destroyAudioPolicyManager
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 59876c6..74b3405 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -109,6 +109,9 @@
const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
return mSecondaryOutputs;
};
+ void setSecondaryOutputs(std::vector<wp<SwAudioOutputDescriptor>>&& secondaryOutputs) {
+ mSecondaryOutputs = std::move(secondaryOutputs);
+ }
VolumeSource volumeSource() const { return mVolumeSource; }
const sp<AudioPolicyMix> getPrimaryMix() const {
return mPrimaryMix.promote();
@@ -143,7 +146,7 @@
const product_strategy_t mStrategy;
const VolumeSource mVolumeSource;
const audio_output_flags_t mFlags;
- const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
+ std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
const wp<AudioPolicyMix> mPrimaryMix;
/**
* required for duplicating thread, prevent from removing active client from an output
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7185435..c8ddbc6 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -31,6 +31,7 @@
#include <algorithm>
#include <inttypes.h>
+#include <map>
#include <math.h>
#include <set>
#include <unordered_set>
@@ -5694,6 +5695,7 @@
void AudioPolicyManager::checkSecondaryOutputs() {
std::set<audio_stream_type_t> streamsToInvalidate;
+ TrackSecondaryOutputsMap trackSecondaryOutputs;
for (size_t i = 0; i < mOutputs.size(); i++) {
const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
@@ -5710,16 +5712,28 @@
}
}
- if (status != OK ||
- !std::equal(client->getSecondaryOutputs().begin(),
- client->getSecondaryOutputs().end(),
- secondaryDescs.begin(), secondaryDescs.end())) {
+ if (status != OK) {
streamsToInvalidate.insert(client->stream());
+ } else if (!std::equal(
+ client->getSecondaryOutputs().begin(),
+ client->getSecondaryOutputs().end(),
+ secondaryDescs.begin(), secondaryDescs.end())) {
+ std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
+ std::vector<audio_io_handle_t> secondaryOutputIds;
+ for (const auto& secondaryDesc : secondaryDescs) {
+ secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
+ weakSecondaryDescs.push_back(secondaryDesc);
+ }
+ trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
+ client->setSecondaryOutputs(std::move(weakSecondaryDescs));
}
}
}
+ if (!trackSecondaryOutputs.empty()) {
+ mpClientInterface->updateSecondaryOutputs(trackSecondaryOutputs);
+ }
for (audio_stream_type_t stream : streamsToInvalidate) {
- ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
+ ALOGD("%s Invalidate stream %d due to fail getting output for attr", __func__, stream);
mpClientInterface->invalidateStream(stream);
}
}
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 77b5200..cd53073 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -291,4 +291,14 @@
return af->getAudioPort(port);
}
+status_t AudioPolicyService::AudioPolicyClient::updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs) {
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ ALOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+ return af->updateSecondaryOutputs(trackSecondaryOutputs);
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 00d9670..6eb33f6 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -735,6 +735,9 @@
status_t getAudioPort(struct audio_port_v7 *port) override;
+ status_t updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs) override;
+
private:
AudioPolicyService *mAudioPolicyService;
};
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index e2d7d17..f7b0565 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -134,6 +134,11 @@
size_t getRoutingUpdatedCounter() const {
return mRoutingUpdatedUpdateCount; }
+ status_t updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs __unused) override {
+ return NO_ERROR;
+ }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index d289e15..1384864 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -91,6 +91,10 @@
status_t getAudioPort(struct audio_port_v7 *port __unused) override {
return INVALID_OPERATION;
};
+ status_t updateSecondaryOutputs(
+ const TrackSecondaryOutputsMap& trackSecondaryOutputs __unused) override {
+ return NO_INIT;
+ }
};
} // namespace android