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
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0eee3f2..02b85b4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -36,6 +36,7 @@
#include <inttypes.h>
#include <math.h>
+#include <set>
#include <unordered_set>
#include <vector>
@@ -907,16 +908,18 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
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 @@
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 @@
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 @@
// 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::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();