In favor of SW patch in AudioPolicyManager::connectAudioSource
In case the dynamic routing rule is present, creates the SW patch from
audio source device to Android mixer (specified by dynamic routing rule)
A typical use case is to route broadcast radio to the same media output
as other media applications.
Bug: 118763832
Test: connectAudioSource from FM Radio and dumpsys media.audio_flinger
Change-Id: Id07d3a6dd9fc5f322f4eb38efe02c302018253cd
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index f07b797..02f6f5a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -780,17 +780,39 @@
return output;
}
-status_t AudioPolicyManager::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 AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr,
+ const audio_attributes_t *srcAttr,
+ audio_stream_type_t srcStream)
{
- audio_attributes_t attributes;
+ if (srcAttr != NULL) {
+ if (!isValidAttributes(srcAttr)) {
+ ALOGE("%s invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]",
+ __func__,
+ srcAttr->usage, srcAttr->content_type, srcAttr->flags,
+ srcAttr->tags);
+ return BAD_VALUE;
+ }
+ *dstAttr = *srcAttr;
+ } else {
+ if (srcStream < AUDIO_STREAM_MIN || srcStream >= AUDIO_STREAM_PUBLIC_CNT) {
+ ALOGE("%s: invalid stream type", __func__);
+ return BAD_VALUE;
+ }
+ stream_type_to_audio_attributes(srcStream, 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)
+{
DeviceVector outputDevices;
routing_strategy strategy;
audio_devices_t device;
@@ -798,35 +820,20 @@
audio_devices_t msdDevice =
getModuleDeviceTypes(mAvailableOutputDevices, AUDIO_HARDWARE_MODULE_ID_MSD);
- // The supplied portId must be AUDIO_PORT_HANDLE_NONE
- if (*portId != AUDIO_PORT_HANDLE_NONE) {
- return INVALID_OPERATION;
+ status_t status = getAudioAttributes(resultAttr, attr, *stream);
+ if (status != NO_ERROR) {
+ return status;
}
- if (attr != NULL) {
- if (!isValidAttributes(attr)) {
- ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]",
- attr->usage, attr->content_type, attr->flags,
- attr->tags);
- return BAD_VALUE;
- }
- attributes = *attr;
- } else {
- if (*stream < AUDIO_STREAM_MIN || *stream >= AUDIO_STREAM_PUBLIC_CNT) {
- ALOGE("getOutputForAttr(): invalid stream type");
- return BAD_VALUE;
- }
- stream_type_to_audio_attributes(*stream, &attributes);
- }
-
- ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
+ ALOGV("%s usage=%d, content=%d, tag=%s flags=%08x"
" session %d selectedDeviceId %d",
- attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+ __func__,
+ resultAttr->usage, resultAttr->content_type, resultAttr->tags, resultAttr->flags,
session, requestedDeviceId);
- *stream = streamTypefromAttributesInt(&attributes);
+ *stream = streamTypefromAttributesInt(resultAttr);
- strategy = getStrategyForAttr(&attributes);
+ strategy = getStrategyForAttr(resultAttr);
// First check for explicit routing (eg. setPreferredDevice)
if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
@@ -836,30 +843,30 @@
} else {
// If no explict route, is there a matching dynamic policy that applies?
sp<SwAudioOutputDescriptor> desc;
- if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
+ if (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;
}
- *stream = streamTypefromAttributesInt(&attributes);
+ *stream = streamTypefromAttributesInt(resultAttr);
*output = desc->mIoHandle;
AudioMix *mix = desc->mPolicyMix;
sp<DeviceDescriptor> deviceDesc =
mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress);
*selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
- ALOGV("getOutputForAttr() returns output %d", *output);
- goto exit;
+ ALOGV("%s returns output %d", __func__, *output);
+ return NO_ERROR;
}
// Virtual sources must always be dynamicaly or explicitly routed
- if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
- ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
+ if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+ ALOGW("%s no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE", __func__);
return BAD_VALUE;
}
device = getDeviceForStrategy(strategy, false /*fromCache*/);
}
- if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
+ if ((resultAttr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
}
@@ -869,7 +876,7 @@
// to getOutputForDevice.
// TODO: Remove check of AUDIO_STREAM_MUSIC once migration is completed on the app side.
if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX &&
- (*stream == AUDIO_STREAM_MUSIC || attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) &&
+ (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) &&
audio_is_linear_pcm(config->format) &&
isInCall()) {
if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
@@ -880,9 +887,9 @@
}
}
- ALOGV("getOutputForAttr() device 0x%x, sampling rate %d, format %#x, channel mask %#x, "
+ ALOGV("%s device 0x%x, sampling rate %d, format %#x, channel mask %#x, "
"flags %#x",
- device, config->sample_rate, config->format, config->channel_mask, *flags);
+ __func__, device, config->sample_rate, config->format, config->channel_mask, *flags);
*output = AUDIO_IO_HANDLE_NONE;
if (msdDevice != AUDIO_DEVICE_NONE) {
@@ -906,22 +913,48 @@
*selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId()
: AUDIO_PORT_HANDLE_NONE;
-exit:
+ ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId);
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::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)
+{
+ // The supplied portId must be AUDIO_PORT_HANDLE_NONE
+ if (*portId != AUDIO_PORT_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
+ const audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ audio_attributes_t resultAttr;
+ status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
+ config, flags, selectedDeviceId);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
.format = config->format,
.channel_mask = config->channel_mask };
*portId = AudioPort::getNextUniqueId();
sp<TrackClientDescriptor> clientDesc =
- new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig,
+ new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
requestedDeviceId, *stream,
- getStrategyForAttr(&attributes),
+ getStrategyForAttr(&resultAttr),
*flags);
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
outputDesc->addClient(clientDesc);
- ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d",
- *output, *selectedDeviceId, *portId);
+ ALOGV("%s returns output %d selectedDeviceId %d for port ID %d",
+ __func__, *output, requestedDeviceId, *portId);
return NO_ERROR;
}
@@ -3400,11 +3433,20 @@
srcDeviceDesc->getAudioPort()->mModule->getHalVersionMajor() >= 3 &&
srcDeviceDesc->getAudioPort()->mGains.size() > 0) {
ALOGV("%s AUDIO_DEVICE_API_VERSION_3_0", __FUNCTION__);
- // create patch between src device and output device
- // create Hwoutput and add to mHwOutputs
+ // TODO: may explicitly specify whether we should use HW or SW patch
+ // create patch between src device and output device
+ // create Hwoutput and add to mHwOutputs
} else {
- SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(sinkDevice, mOutputs);
- audio_io_handle_t output = selectOutput(outputs);
+ audio_attributes_t resultAttr;
+ audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.sample_rate = sourceDesc->config().sample_rate;
+ config.channel_mask = sourceDesc->config().channel_mask;
+ config.format = sourceDesc->config().format;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
+ &attributes, &stream, sourceDesc->uid(), &config, &flags, &selectedDeviceId);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevice);
return INVALID_OPERATION;
@@ -3437,6 +3479,13 @@
__FUNCTION__, status);
return INVALID_OPERATION;
}
+
+ if (outputDesc->getClient(sourceDesc->portId()) != nullptr) {
+ ALOGW("%s source portId has already been attached to outputDesc", __func__);
+ return INVALID_OPERATION;
+ }
+ outputDesc->addClient(sourceDesc);
+
uint32_t delayMs = 0;
status = startSource(outputDesc, sourceDesc, &delayMs);