Add secondary output to audio tracks

The secondary are returned from mixes (from DAP loopback&render),
from getOutputForAttr.
All getOutputForAttr* of the stack are update.
Internal getOutputForAttr use descriptor, external one use handles.

The secondary output are saved in each track and the track is
invalidated if the list of secondary output changes.

In audio flinger, create a pair of recordTrack & patchTrack to pipe
the intercepted audio audio to the secondary output.

Test: adb shell audiorecorder --target /data/file.raw
Bug: 111453086
Change-Id: Id6523d9e383c15a0e39313d5f355df809b7e72fe
gugelfrei
Kevin Rocard 6 years ago
parent 45986c74cd
commit 153f92d57b

@ -872,13 +872,14 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
config,
flags, selectedDeviceId, portId);
flags, selectedDeviceId, portId, secondaryOutputs);
}
status_t AudioSystem::startOutput(audio_port_handle_t portId)

@ -189,16 +189,17 @@ public:
return static_cast <audio_io_handle_t> (reply.readInt32());
}
virtual status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
pid_t pid,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
pid_t pid,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs) override
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@ -224,6 +225,10 @@ public:
ALOGE("getOutputForAttr NULL portId - shouldn't happen");
return BAD_VALUE;
}
if (secondaryOutputs == NULL) {
ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen");
return BAD_VALUE;
}
if (attr == NULL) {
data.writeInt32(0);
} else {
@ -258,7 +263,9 @@ public:
}
*selectedDeviceId = (audio_port_handle_t)reply.readInt32();
*portId = (audio_port_handle_t)reply.readInt32();
return status;
secondaryOutputs->resize(reply.readInt32());
return reply.read(secondaryOutputs->data(),
secondaryOutputs->size() * sizeof(audio_io_handle_t));
}
virtual status_t startOutput(audio_port_handle_t portId)
@ -1300,16 +1307,19 @@ status_t BnAudioPolicyService::onTransact(
audio_port_handle_t selectedDeviceId = data.readInt32();
audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
audio_io_handle_t output = 0;
std::vector<audio_io_handle_t> secondaryOutputs;
status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
&output, session, &stream, pid, uid,
&config,
flags, &selectedDeviceId, &portId);
flags, &selectedDeviceId, &portId, &secondaryOutputs);
reply->writeInt32(status);
reply->writeInt32(output);
reply->writeInt32(stream);
reply->writeInt32(selectedDeviceId);
reply->writeInt32(portId);
return NO_ERROR;
reply->writeInt32(secondaryOutputs.size());
return reply->write(secondaryOutputs.data(),
secondaryOutputs.size() * sizeof(audio_io_handle_t));
} break;
case START_OUTPUT: {

@ -231,7 +231,8 @@ public:
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs);
static status_t startOutput(audio_port_handle_t portId);
static status_t stopOutput(audio_port_handle_t portId);
static void releaseOutput(audio_port_handle_t portId);

@ -66,7 +66,8 @@ public:
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
virtual status_t startOutput(audio_port_handle_t portId) = 0;
virtual status_t stopOutput(audio_port_handle_t portId) = 0;
virtual void releaseOutput(audio_port_handle_t portId) = 0;

@ -292,13 +292,16 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di
fullConfig.sample_rate = config->sample_rate;
fullConfig.channel_mask = config->channel_mask;
fullConfig.format = config->format;
std::vector<audio_io_handle_t> secondaryOutputs;
ret = AudioSystem::getOutputForAttr(attr, &io,
actualSessionId,
&streamType, client.clientPid, client.clientUid,
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
deviceId, &portId);
deviceId, &portId, &secondaryOutputs);
ALOGW_IF(!secondaryOutputs.empty(),
"%s does not support secondary outputs, ignoring them", __func__);
} else {
ret = AudioSystem::getInputForAttr(attr, &io,
actualSessionId,
@ -678,6 +681,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
status_t lStatus;
audio_stream_type_t streamType;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
std::vector<audio_io_handle_t> secondaryOutputs;
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@ -712,7 +716,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
clientPid, clientUid, &input.config, input.flags,
&output.selectedDeviceId, &portId);
&output.selectedDeviceId, &portId, &secondaryOutputs);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@ -785,6 +789,61 @@ sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
output.afLatencyMs = thread->latency();
output.portId = portId;
if (lStatus == NO_ERROR) {
// 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 frameCount = std::lcm(thread->frameCount(), secondaryThread->frameCount());
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;
}
sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
streamType,
output.sampleRate,
input.config.channel_mask,
input.config.format,
frameCount,
patchRecord->buffer(),
patchRecord->bufferSize(),
output.flags,
0ns /* timeout */);
status = patchTrack->initCheck();
if (status != NO_ERROR) {
ALOGE("Secondary output patchTrack init failed: %d", status);
continue;
}
teePatches.push_back({patchRecord, patchTrack});
secondaryThread->addPatchTrack(patchTrack);
patchTrack->setPeerProxy(patchRecord.get());
patchRecord->setPeerProxy(patchTrack.get());
patchTrack->start(); // patchRecord is NOT started as it has no thread
}
track->setTeePatches(std::move(teePatches));
}
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
if (lStatus == NO_ERROR && effectThread != NULL) {

@ -24,6 +24,7 @@
#include <chrono>
#include <deque>
#include <map>
#include <numeric>
#include <optional>
#include <set>
#include <string>
@ -528,6 +529,9 @@ private:
class EffectChain;
struct AudioStreamIn;
struct TeePatch;
using TeePatches = std::vector<TeePatch>;
struct stream_type_t {
stream_type_t()
@ -727,6 +731,11 @@ using effect_buffer_t = int16_t;
audioHwDev(dev), stream(in), flags(flags) {}
};
struct TeePatch {
sp<RecordThread::PatchRecord> patchRecord;
sp<PlaybackThread::PatchTrack> patchTrack;
};
// for mAudioSessionRefs only
struct AudioSessionRef {
AudioSessionRef(audio_session_t sessionid, pid_t pid) :

@ -43,9 +43,8 @@ public:
void appendDumpHeader(String8& result);
void appendDump(String8& result, bool active);
virtual status_t start(AudioSystem::sync_event_t event =
AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
virtual void stop();
void pause();
@ -129,6 +128,8 @@ public:
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
void setTeePatches(TeePatches teePatches);
protected:
// for numerous
friend class PlaybackThread;
@ -139,8 +140,8 @@ protected:
DISALLOW_COPY_AND_ASSIGN(Track);
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
// releaseBuffer() not overridden
status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
// ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
@ -220,6 +221,8 @@ protected:
sp<os::ExternalVibration> mExternalVibration;
private:
void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
// The following fields are only for fast tracks, and should be in a subclass
int mFastIndex; // index within FastMixerState::mFastTracks[];
// either mFastIndex == -1 if not isFastTrack()
@ -239,6 +242,7 @@ private:
audio_output_flags_t mFlags;
// If the last track change was notified to the client with readAndClearHasChanged
std::atomic_flag mChangeNotified = ATOMIC_FLAG_INIT;
TeePatches mTeePatches;
}; // end of Track

@ -8464,6 +8464,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client,
audio_output_flags_t flags =
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId = mDeviceId;
std::vector<audio_io_handle_t> secondaryOutputs;
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
@ -8472,7 +8473,10 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client,
&config,
flags,
&deviceId,
&portId);
&portId,
&secondaryOutputs);
ALOGD_IF(!secondaryOutputs.empty(),
"MmapThread::start does not support secondary outputs, ignoring them");
} else {
audio_config_base_t config;
config.sample_rate = mSampleRate;

@ -99,7 +99,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false),
mType(type),
mThreadIoHandle(thread->id()),
mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE),
mPortId(portId),
mIsInvalid(false)
{
@ -670,8 +670,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
}
// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
ServerProxy::Buffer buf;
size_t desiredFrames = buffer->frameCount;
@ -686,10 +685,39 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
} else {
mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
return status;
}
void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
interceptBuffer(*buffer);
TrackBase::releaseBuffer(buffer);
}
// TODO: compensate for time shift between HW modules.
void AudioFlinger::PlaybackThread::Track::interceptBuffer(
const AudioBufferProvider::Buffer& buffer) {
for (auto& sink : mTeePatches) {
RecordThread::PatchRecord& patchRecord = *sink.patchRecord;
AudioBufferProvider::Buffer patchBuffer;
patchBuffer.frameCount = buffer.frameCount;
auto status = patchRecord.getNextBuffer(&patchBuffer);
if (status != NO_ERROR) {
ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
__func__, status, strerror(-status));
continue;
}
// FIXME: On buffer wrap, the frame count will be less then requested,
// retry to write the rest. (unlikely due to lcm buffer sizing)
ALOGW_IF(patchBuffer.frameCount != buffer.frameCount,
"%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames",
__func__, patchBuffer.frameCount, buffer.frameCount,
buffer.frameCount - patchBuffer.frameCount);
memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize);
patchRecord.releaseBuffer(&patchBuffer);
}
}
// releaseBuffer() is not overridden
// ExtendedAudioBufferProvider interface
@ -1081,6 +1109,10 @@ void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backI
};
}
void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
mTeePatches = std::move(teePatches);
}
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
if (!isOffloaded() && !isDirect()) {

@ -110,7 +110,8 @@ public:
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
// indicates to the audio policy manager that the output stops being used by corresponding stream.

@ -68,13 +68,12 @@ public:
* Try to find an output descriptor for the given attributes.
*
* @param[in] attributes to consider fowr the research of output descriptor.
* @param[out] desc to return if an output could be found.
*
* @return NO_ERROR if an output was found for the given attribute (in this case, the
* descriptor output param is initialized), error code otherwise.
* @param[out] desc to return if an primary output could be found.
* @param[out] secondaryDesc other desc that the audio should be routed to.
*/
status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
sp<SwAudioOutputDescriptor> &desc);
sp<SwAudioOutputDescriptor> &primaryDesc,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
const DeviceVector &availableDeviceTypes,

@ -86,10 +86,12 @@ public:
audio_attributes_t attributes, audio_config_base_t config,
audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
product_strategy_t strategy, audio_output_flags_t flags,
bool isPreferredDeviceForExclusiveUse) :
bool isPreferredDeviceForExclusiveUse,
std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
isPreferredDeviceForExclusiveUse),
mStream(stream), mStrategy(strategy), mFlags(flags) {}
mStream(stream), mStrategy(strategy), mFlags(flags),
mSecondaryOutputs(std::move(secondaryOutputs)) {}
~TrackClientDescriptor() override = default;
using ClientDescriptor::dump;
@ -99,11 +101,15 @@ public:
audio_output_flags_t flags() const { return mFlags; }
audio_stream_type_t stream() const { return mStream; }
product_strategy_t strategy() const { return mStrategy; }
const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
return mSecondaryOutputs;
};
private:
const audio_stream_type_t mStream;
const product_strategy_t mStrategy;
const audio_output_flags_t mFlags;
const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
};
class RecordClientDescriptor: public ClientDescriptor

@ -157,30 +157,49 @@ void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
}
status_t AudioPolicyMixCollection::getOutputForAttr(
audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &desc)
audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
{
ALOGV("getOutputForAttr() querying %zu mixes:", size());
desc = 0;
primaryDesc = 0;
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
if (!policyDesc) {
ALOGV("Skiping %zu: Mix has no output", i);
ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
continue;
}
AudioMix *mix = policyMix->getMix();
const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags);
if (primaryOutputMix && primaryDesc != 0) {
ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
continue; // Primary output already found
}
switch (mixMatch(mix, i, attributes, uid)) {
case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort ?
case MixMatchStatus::NO_MATCH: continue; // skip the mix
case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort?
case MixMatchStatus::NO_MATCH:
ALOGV("%s: Mix %zu: does not match", __func__, i);
continue; // skip the mix
case MixMatchStatus::MATCH:;
}
desc = policyMix->getOutput();
desc->mPolicyMix = mix;
return NO_ERROR;
policyDesc->mPolicyMix = mix;
if (primaryOutputMix) {
primaryDesc = policyDesc;
ALOGV("%s: Mix %zu: set primary desc", __func__, i);
} else {
if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
} else {
ALOGV("%s: Add a secondary desc %zu", __func__, i);
secondaryDescs->push_back(policyDesc);
}
}
}
return BAD_VALUE;
return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
}
AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(

@ -85,7 +85,8 @@ SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t
product_strategy_t strategy) :
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false),
stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false,
{} /* Sources do not support secondary outputs*/),
mPatchDesc(patchDesc), mSrcDevice(srcDevice)
{
}

@ -36,6 +36,7 @@
#include <inttypes.h>
#include <math.h>
#include <set>
#include <unordered_set>
#include <vector>
@ -907,16 +908,18 @@ status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr,
return NO_ERROR;
}
status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr,
audio_io_handle_t *output,
audio_session_t session,
const audio_attributes_t *attr,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse)
status_t AudioPolicyManager::getOutputForAttrInt(
audio_attributes_t *resultAttr,
audio_io_handle_t *output,
audio_session_t session,
const audio_attributes_t *attr,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
{
DeviceVector outputDevices;
const audio_port_handle_t requestedPortId = *selectedDeviceId;
@ -935,19 +938,26 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr,
ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__,
toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId);
// 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine
// 2/ If no explict route, is there a matching dynamic policy that applies?
// NOTE: new engine product strategy does not make use of dynamic routing, keep it for
// remote-submix and legacy
sp<SwAudioOutputDescriptor> desc;
if (requestedDevice == nullptr &&
mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_has_proportional_frames(config->format)) {
return BAD_VALUE;
}
*output = desc->mIoHandle;
AudioMix *mix = desc->mPolicyMix;
// The primary output is the explicit routing (eg. setPreferredDevice) if specified,
// otherwise, fallback to the dynamic policies, if none match, query the engine.
// Secondary outputs are always found by dynamic policies as the engine do not support them
sp<SwAudioOutputDescriptor> policyDesc;
if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) {
policyDesc = nullptr; // reset getOutputForAttr in case of failure
secondaryDescs->clear();
}
// Explicit routing is higher priority then any dynamic policy primary output
bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
// FIXME: in case of RENDER policy, the output capabilities should be checked
if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
&& !audio_has_proportional_frames(config->format)) {
ALOGW("%s: audio loopback only supports proportional frames", __func__);
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
*output = policyDesc->mIoHandle;
AudioMix *mix = policyDesc->mPolicyMix;
sp<DeviceDescriptor> deviceDesc =
mAvailableOutputDevices.getDevice(mix->mDeviceType,
mix->mDeviceAddress,
@ -1022,7 +1032,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs)
{
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
if (*portId != AUDIO_PORT_HANDLE_NONE) {
@ -1031,11 +1042,18 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
const audio_port_handle_t requestedPortId = *selectedDeviceId;
audio_attributes_t resultAttr;
bool isRequestedDeviceForExclusiveUse = false;
std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse);
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
&secondaryOutputDescs);
if (status != NO_ERROR) {
return status;
}
std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryOutputDescs;
for (auto& secondaryDesc : secondaryOutputDescs) {
secondaryOutputs->push_back(secondaryDesc->mIoHandle);
weakSecondaryOutputDescs.push_back(secondaryDesc);
}
audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
.format = config->format,
@ -1046,7 +1064,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
requestedPortId, *stream,
mEngine->getProductStrategyForAttributes(resultAttr),
*flags, isRequestedDeviceForExclusiveUse);
*flags, isRequestedDeviceForExclusiveUse,
std::move(weakSecondaryOutputDescs));
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
outputDesc->addClient(clientDesc);
@ -1562,13 +1581,15 @@ status_t AudioPolicyManager::startSource(const sp<SwAudioOutputDescriptor>& outp
policyMix = outputDesc->mPolicyMix;
audio_devices_t newDeviceType;
address = policyMix->mDeviceAddress.string();
if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
newDeviceType = policyMix->mDeviceType;
} else {
if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
} else {
newDeviceType = policyMix->mDeviceType;
}
devices.add(mAvailableOutputDevices.getDevice(newDeviceType,
String8(address), AUDIO_FORMAT_DEFAULT));
sp device = mAvailableOutputDevices.getDevice(newDeviceType, String8(address),
AUDIO_FORMAT_DEFAULT);
ALOG_ASSERT(device, "%s: no device found t=%u, a=%s", __func__, newDeviceType, address);
devices.add(device);
}
// requiresMuteCheck is false when we can bypass mute strategy.
@ -2609,18 +2630,24 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector<AudioMix>& mixes)
// examine each mix's route type
for (size_t i = 0; i < mixes.size(); i++) {
AudioMix mix = mixes[i];
// we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
// Only capture of playback is allowed in LOOP_BACK & RENDER mode
if (is_mix_loopback_render(mix.mRouteFlags) && mix.mMixType != MIX_TYPE_PLAYERS) {
ALOGE("Unsupported Policy Mix %zu of %zu: "
"Only capture of playback is allowed in LOOP_BACK & RENDER mode",
i, mixes.size());
res = INVALID_OPERATION;
break;
}
// LOOP_BACK and LOOP_BACK | RENDER have the same remote submix backend and are handled
// in the same way.
if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK %d", i, mixes.size(),
mix.mRouteFlags);
if (rSubmixModule == 0) {
rSubmixModule = mHwModules.getModuleFromName(
AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX);
if (rSubmixModule == 0) {
ALOGE(" Unable to find audio module for submix, aborting mix %zu registration",
ALOGE("Unable to find audio module for submix, aborting mix %zu registration",
i);
res = INVALID_OPERATION;
break;
@ -2635,7 +2662,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector<AudioMix>& mixes)
}
if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
ALOGE(" Error registering mix %zu for address %s", i, address.string());
ALOGE("Error registering mix %zu for address %s", i, address.string());
res = INVALID_OPERATION;
break;
}
@ -2679,6 +2706,8 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector<AudioMix>& mixes)
if (desc->supportedDevices().contains(device)) {
if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type,
address.string());
res = INVALID_OPERATION;
} else {
foundOutput = true;
@ -2746,7 +2775,7 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector<AudioMix> mixes)
rSubmixModule->removeOutputProfile(address);
rSubmixModule->removeInputProfile(address);
} if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
} else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) {
res = INVALID_OPERATION;
continue;
@ -3635,9 +3664,11 @@ status_t AudioPolicyManager::connectAudioSource(const sp<SourceClientDescriptor>
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
bool isRequestedDeviceForExclusiveUse = false;
std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
&attributes, &stream, sourceDesc->uid(), &config, &flags,
&selectedDeviceId, &isRequestedDeviceForExclusiveUse);
&selectedDeviceId, &isRequestedDeviceForExclusiveUse,
&secondaryOutputs);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types());
return INVALID_OPERATION;
@ -4782,6 +4813,7 @@ void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function<bool()> on
// output is suspended before any tracks are moved to it
checkA2dpSuspend();
checkOutputForAllStrategies();
checkSecondaryOutputs();
if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
updateDevicesAndOutputs();
if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
@ -4870,6 +4902,29 @@ void AudioPolicyManager::checkOutputForAllStrategies()
}
}
void AudioPolicyManager::checkSecondaryOutputs() {
std::set<audio_stream_type_t> streamsToInvalidate;
for (size_t i = 0; i < mOutputs.size(); i++) {
const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
// FIXME code duplicated from getOutputForAttrInt
sp<SwAudioOutputDescriptor> desc;
std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc,
&secondaryDescs);
if (!std::equal(client->getSecondaryOutputs().begin(),
client->getSecondaryOutputs().end(),
secondaryDescs.begin(), secondaryDescs.end())) {
streamsToInvalidate.insert(client->stream());
}
}
}
for (audio_stream_type_t stream : streamsToInvalidate) {
ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
mpClientInterface->invalidateStream(stream);
}
}
void AudioPolicyManager::checkA2dpSuspend()
{
audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput();

@ -113,15 +113,16 @@ public:
virtual void setSystemProperty(const char* property, const char* value);
virtual status_t initCheck();
virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
virtual status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual void releaseOutput(audio_port_handle_t portId);
@ -431,6 +432,10 @@ protected:
*/
void checkOutputForAllStrategies();
// Same as checkOutputForStrategy but for secondary outputs. Make sure if a secondary
// output condition changes, the track is properly rerouted
void checkSecondaryOutputs();
// manages A2DP output suspend/restore according to phone state and BT SCO usage
void checkA2dpSuspend();
@ -711,7 +716,8 @@ private:
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse);
bool *isRequestedDeviceForExclusiveUse,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
// internal method to return the output handle for the given device and format
audio_io_handle_t getOutputForDevices(
const DeviceVector &devices,

@ -175,7 +175,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
@ -193,7 +194,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
AutoCallerClear acc;
status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config,
&flags, selectedDeviceId, portId);
&flags, selectedDeviceId, portId,
secondaryOutputs);
// FIXME: Introduce a way to check for the the telephony device before opening the output
if ((result == NO_ERROR) &&
@ -205,9 +207,10 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
flags = originalFlags;
*selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
*portId = AUDIO_PORT_HANDLE_NONE;
result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
config,
&flags, selectedDeviceId, portId);
secondaryOutputs->clear();
result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
&flags, selectedDeviceId, portId,
secondaryOutputs);
}
if (result == NO_ERROR) {

@ -74,16 +74,17 @@ public:
virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
virtual status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
pid_t pid,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
status_t getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
pid_t pid,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual void releaseOutput(audio_port_handle_t portId);

@ -214,7 +214,7 @@ void AudioPolicyManagerTest::getOutputForAttr(
*portId = AUDIO_PORT_HANDLE_NONE;
ASSERT_EQ(OK, mManager->getOutputForAttr(
&attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
selectedDeviceId, portId));
selectedDeviceId, portId, {}));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}

Loading…
Cancel
Save