audio policy: refactor preferred route implementation
Remove SessionRoute class and replace by use of
preferredDevice and active state in ClientDescriptor
Also fix the behavior for other clients using same strategy as
client with preferred route: the preferred route is only used if
no other clients using the same strategy are active without a preferred
route.
If all clients have a preferred route, the route of the last active
client is used.
Also fixed getOutputForAttr() to return correct selected device ID when
dynamic policies are used.
Test: CTS RoutingTest, AudioPlayRoutingNative, AudioRecordRoutingNative
Test: Manual: AudioExplicitRouting, UplinkMusicSampleApp
Change-Id: Id342c2241ace3e1a359ea74f4a539a395f793b86
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e4f3cf1..cd40bc5 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -788,7 +788,7 @@
DeviceVector outputDevices;
routing_strategy strategy;
audio_devices_t device;
- audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ const audio_port_handle_t requestedDeviceId = *selectedDeviceId;
audio_devices_t msdDevice = getMsdAudioOutDeviceTypes();
if (attr != NULL) {
@@ -808,19 +808,24 @@
}
ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
- " session %d selectedDeviceId %d",
- attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
- session, *selectedDeviceId);
+ " session %d selectedDeviceId %d",
+ attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+ session, requestedDeviceId);
// TODO: check for existing client for this port ID
if (*portId == AUDIO_PORT_HANDLE_NONE) {
*portId = AudioPort::getNextUniqueId();
}
+ *stream = streamTypefromAttributesInt(&attributes);
+
+ strategy = getStrategyForAttr(&attributes);
+
// First check for explicit routing (eg. setPreferredDevice)
- sp<DeviceDescriptor> deviceDesc;
- if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
- deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+ if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ sp<DeviceDescriptor> deviceDesc =
+ mAvailableOutputDevices.getDeviceFromId(requestedDeviceId);
+ device = deviceDesc->type();
} else {
// If no explict route, is there a matching dynamic policy that applies?
sp<SwAudioOutputDescriptor> desc;
@@ -831,6 +836,10 @@
}
*stream = streamTypefromAttributesInt(&attributes);
*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;
}
@@ -840,25 +849,9 @@
ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
return BAD_VALUE;
}
+ device = getDeviceForStrategy(strategy, false /*fromCache*/);
}
- // 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");
- return BAD_VALUE;
- }
-
- *stream = streamTypefromAttributesInt(&attributes);
-
- // TODO: Should this happen only if an explicit route is active?
- // the previous code structure meant that this would always happen which
- // would appear to result in adding a null deviceDesc when not using an
- // explicit route. Is that the intended and necessary behavior?
- mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
-
- strategy = (routing_strategy) getStrategyForAttr(&attributes);
- device = getDeviceForStrategy(strategy, false /*fromCache*/);
-
if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
}
@@ -871,9 +864,10 @@
*stream == AUDIO_STREAM_MUSIC &&
audio_is_linear_pcm(config->format) &&
isInCall()) {
- if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
*flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
} else {
+ // Get the devce type directly from the engine to bypass preferred route logic
device = mEngine->getDeviceForStrategy(strategy);
}
}
@@ -897,7 +891,6 @@
*output = getOutputForDevice(device, session, *stream, config, flags);
}
if (*output == AUDIO_IO_HANDLE_NONE) {
- mOutputRoutes.removeRoute(session);
return INVALID_OPERATION;
}
@@ -910,10 +903,12 @@
.format = config->format,
.channel_mask = config->channel_mask };
sp<TrackClientDescriptor> clientDesc =
- new TrackClientDescriptor(*portId, uid, session,
- attributes, clientConfig, requestedDeviceId, *stream, *flags);
+ new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig,
+ requestedDeviceId, *stream,
+ getStrategyForAttr(&attributes),
+ *flags);
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
- outputDesc->clients().emplace(*portId, clientDesc);
+ outputDesc->clientsMap().emplace(*portId, clientDesc);
ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d",
*output, *selectedDeviceId, *portId);
@@ -1325,60 +1320,23 @@
ALOGW("startOutput() no output for client %d", portId);
return BAD_VALUE;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_stream_type_t stream = client->stream();
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
ALOGV("startOutput() output %d, stream %d, session %d",
- outputDesc->mIoHandle, stream, session);
+ outputDesc->mIoHandle, client->stream(), client->session());
status_t status = outputDesc->start();
if (status != NO_ERROR) {
return status;
}
- // Routing?
- mOutputRoutes.incRouteActivity(session);
-
- audio_devices_t newDevice;
- AudioMix *policyMix = NULL;
- const char *address = NULL;
- if (outputDesc->mPolicyMix != NULL) {
- policyMix = outputDesc->mPolicyMix;
- address = policyMix->mDeviceAddress.string();
- if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
- newDevice = policyMix->mDeviceType;
- } else {
- newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
- }
- } else if (mOutputRoutes.getAndClearRouteChanged(session)) {
- newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
- if (newDevice != outputDesc->device()) {
- checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
- }
- } else {
- newDevice = AUDIO_DEVICE_NONE;
- }
-
- uint32_t delayMs = 0;
-
- status = startSource(outputDesc, stream, newDevice, address, &delayMs);
+ uint32_t delayMs;
+ status = startSource(outputDesc, client, &delayMs);
if (status != NO_ERROR) {
- mOutputRoutes.decRouteActivity(session);
outputDesc->stop();
return status;
}
- // Automatically enable the remote submix input when output is started on a re routing mix
- // of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(newDevice) && policyMix != NULL &&
- policyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- address,
- "remote-submix");
- }
-
if (delayMs != 0) {
usleep(delayMs * 1000);
}
@@ -1386,16 +1344,15 @@
return status;
}
-status_t AudioPolicyManager::startSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- audio_devices_t device,
- const char *address,
- uint32_t *delayMs)
+status_t AudioPolicyManager::startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client,
+ uint32_t *delayMs)
{
// cannot start playback of STREAM_TTS if any other output is being used
uint32_t beaconMuteLatency = 0;
*delayMs = 0;
+ audio_stream_type_t stream = client->stream();
if (stream == AUDIO_STREAM_TTS) {
ALOGV("\t found BEACON stream");
if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -1413,6 +1370,19 @@
bool force = !outputDesc->isActive() &&
(outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
+ audio_devices_t device = AUDIO_DEVICE_NONE;
+ AudioMix *policyMix = NULL;
+ const char *address = NULL;
+ if (outputDesc->mPolicyMix != NULL) {
+ policyMix = outputDesc->mPolicyMix;
+ address = policyMix->mDeviceAddress.string();
+ if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+ device = policyMix->mDeviceType;
+ } else {
+ device = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ }
+ }
+
// requiresMuteCheck is false when we can bypass mute strategy.
// It covers a common case when there is no materially active audio
// and muting would result in unnecessary delay and dropped audio.
@@ -1423,6 +1393,14 @@
// NOTE that the usage count is the same for duplicated output and hardware output which is
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
outputDesc->changeRefCount(stream, 1);
+ client->setActive(true);
+
+ if (client->hasPreferredDevice(true)) {
+ device = getNewOutputDevice(outputDesc, false /*fromCache*/);
+ if (device != outputDesc->device()) {
+ checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
+ }
+ }
if (stream == AUDIO_STREAM_MUSIC) {
selectOutputForMusicEffects();
@@ -1514,6 +1492,16 @@
setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc);
}
+ // Automatically enable the remote submix input when output is started on a re routing mix
+ // of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(device) && policyMix != NULL &&
+ policyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address,
+ "remote-submix");
+ }
+
return NO_ERROR;
}
@@ -1526,37 +1514,12 @@
ALOGW("stopOutput() no output for client %d", portId);
return BAD_VALUE;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_stream_type_t stream = client->stream();
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
- ALOGV("stopOutput() output %d, stream %d, session %d", outputDesc->mIoHandle, stream, session);
+ ALOGV("stopOutput() output %d, stream %d, session %d",
+ outputDesc->mIoHandle, client->stream(), client->session());
- if (outputDesc->mRefCount[stream] == 1) {
- // Automatically disable the remote submix input when output is stopped on a
- // re routing mix of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(outputDesc->mDevice) &&
- outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- outputDesc->mPolicyMix->mDeviceAddress,
- "remote-submix");
- }
- }
-
- // Routing?
- bool forceDeviceUpdate = false;
- if (outputDesc->mRefCount[stream] > 0) {
- int activityCount = mOutputRoutes.decRouteActivity(session);
- forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0));
-
- if (forceDeviceUpdate) {
- checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
- }
- }
-
- status_t status = stopSource(outputDesc, stream, forceDeviceUpdate);
+ status_t status = stopSource(outputDesc, client);
if (status == NO_ERROR ) {
outputDesc->stop();
@@ -1564,16 +1527,36 @@
return status;
}
-status_t AudioPolicyManager::stopSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- bool forceDeviceUpdate)
+status_t AudioPolicyManager::stopSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client)
{
// always handle stream stop, check which stream type is stopping
+ audio_stream_type_t stream = client->stream();
+
handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
if (outputDesc->mRefCount[stream] > 0) {
+ if (outputDesc->mRefCount[stream] == 1) {
+ // Automatically disable the remote submix input when output is stopped on a
+ // re routing mix of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+ outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ outputDesc->mPolicyMix->mDeviceAddress,
+ "remote-submix");
+ }
+ }
+ bool forceDeviceUpdate = false;
+ if (client->hasPreferredDevice(true)) {
+ checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
+ forceDeviceUpdate = true;
+ }
+
// decrement usage count of this stream on the output
outputDesc->changeRefCount(stream, -1);
+ client->setActive(false);
// store time at which the stream was stopped - see isStreamActive()
if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) {
@@ -1636,14 +1619,10 @@
ALOGW("releaseOutput() no output for client %d", portId);
return;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
ALOGV("releaseOutput() %d", outputDesc->mIoHandle);
- // Routing
- mOutputRoutes.removeRoute(session);
-
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (outputDesc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1655,7 +1634,7 @@
mpClientInterface->onAudioPortListUpdate();
}
}
- outputDesc->clients().erase(portId);
+ outputDesc->clientsMap().erase(portId);
}
@@ -1693,7 +1672,6 @@
if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId);
}
- mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
// special case for mmap capture: if an input IO handle is specified, we reuse this input if
// possible
@@ -1763,7 +1741,11 @@
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
address = String8(attr->tags + strlen("addr="));
} else {
- device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+ if (deviceDesc != 0) {
+ device = deviceDesc->type();
+ } else {
+ device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+ }
if (device == AUDIO_DEVICE_NONE) {
ALOGW("getInputForAttr() could not find device for source %d", inputSource);
status = BAD_VALUE;
@@ -1808,7 +1790,7 @@
clientDesc = new RecordClientDescriptor(*portId, uid, session,
*attr, *config, requestedDeviceId, inputSource, flags);
inputDesc = mInputs.valueFor(*input);
- inputDesc->clients().emplace(*portId, clientDesc);
+ inputDesc->clientsMap().emplace(*portId, clientDesc);
ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d",
*input, *inputType, *selectedDeviceId, *portId);
@@ -1816,7 +1798,6 @@
return NO_ERROR;
error:
- mInputRoutes.removeRoute(session);
return status;
}
@@ -2027,7 +2008,7 @@
ALOGW("startInput() no input for client %d", portId);
return BAD_VALUE;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
audio_session_t session = client->session();
audio_io_handle_t input = inputDesc->mIoHandle;
@@ -2137,11 +2118,9 @@
// increment activity count before calling getNewInputDevice() below as only active sessions
// are considered for device selection
inputDesc->changeRefCount(session, 1);
+ client->setActive(true);
- // Routing?
- mInputRoutes.incRouteActivity(session);
-
- if (audioSession->activeCount() == 1 || mInputRoutes.getAndClearRouteChanged(session)) {
+ if (audioSession->activeCount() == 1 || client->hasPreferredDevice(true)) {
// indicate active capture to sound trigger service if starting capture from a mic on
// primary HW module
audio_devices_t device = getNewInputDevice(inputDesc);
@@ -2149,8 +2128,8 @@
status_t status = inputDesc->start();
if (status != NO_ERROR) {
- mInputRoutes.decRouteActivity(session);
inputDesc->changeRefCount(session, -1);
+ client->setActive(false);
return status;
}
@@ -2201,7 +2180,7 @@
ALOGW("stopInput() no input for client %d", portId);
return BAD_VALUE;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
audio_session_t session = client->session();
audio_io_handle_t input = inputDesc->mIoHandle;
@@ -2215,9 +2194,7 @@
}
inputDesc->changeRefCount(session, -1);
-
- // Routing?
- mInputRoutes.decRouteActivity(session);
+ client->setActive(false);
if (audioSession->activeCount() == 0) {
inputDesc->stop();
@@ -2272,15 +2249,12 @@
ALOGW("releaseInput() no input for client %d", portId);
return;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
audio_session_t session = client->session();
audio_io_handle_t input = inputDesc->mIoHandle;
ALOGV("releaseInput() %d", input);
- // Routing
- mInputRoutes.removeRoute(session);
-
sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
if (audioSession == 0) {
ALOGW("releaseInput() unknown session %d on input %d", session, input);
@@ -2303,7 +2277,7 @@
}
closeInput(input);
- inputDesc->clients().erase(portId);
+ inputDesc->clientsMap().erase(portId);
mpClientInterface->onAudioPortListUpdate();
ALOGV("releaseInput() exit");
}
@@ -2342,7 +2316,6 @@
}
inputDesc->close();
}
- mInputRoutes.clear();
mInputs.clear();
SoundTrigger::setCaptureState(false);
nextAudioPortGeneration();
@@ -2619,12 +2592,13 @@
sp<HwModule> rSubmixModule;
// 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 ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+ if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
res = INVALID_OPERATION;
break;
}
- if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
+ 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());
if (rSubmixModule == 0) {
rSubmixModule = mHwModules.getModuleFromName(
@@ -2637,15 +2611,20 @@
}
}
- String8 address = mixes[i].mDeviceAddress;
+ String8 address = mix.mDeviceAddress;
+ if (mix.mMixType == MIX_TYPE_PLAYERS) {
+ mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+ } else {
+ mix.mDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ }
- if (mPolicyMixes.registerMix(address, mixes[i], 0 /*output desc*/) != NO_ERROR) {
+ if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
ALOGE(" Error registering mix %zu for address %s", i, address.string());
res = INVALID_OPERATION;
break;
}
- audio_config_t outputConfig = mixes[i].mFormat;
- audio_config_t inputConfig = mixes[i].mFormat;
+ audio_config_t outputConfig = mix.mFormat;
+ audio_config_t inputConfig = mix.mFormat;
// NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in
// stereo and let audio flinger do the channel conversion if needed.
outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
@@ -2655,7 +2634,7 @@
rSubmixModule->addInputProfile(address, &inputConfig,
AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
- if (mixes[i].mMixType == MIX_TYPE_PLAYERS) {
+ if (mix.mMixType == MIX_TYPE_PLAYERS) {
setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
address.string(), "remote-submix");
@@ -2664,9 +2643,9 @@
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
address.string(), "remote-submix");
}
- } else if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
- String8 address = mixes[i].mDeviceAddress;
- audio_devices_t device = mixes[i].mDeviceType;
+ } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+ String8 address = mix.mDeviceAddress;
+ audio_devices_t device = mix.mDeviceType;
ALOGV(" registerPolicyMixes() mix %zu of %zu is RENDER, dev=0x%X addr=%s",
i, mixes.size(), device, address.string());
@@ -2679,7 +2658,7 @@
&& (patch->mPatch.sinks[0].ext.device.type == device)
&& (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(),
AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
- if (mPolicyMixes.registerMix(address, mixes[i], desc) != NO_ERROR) {
+ if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
res = INVALID_OPERATION;
} else {
foundOutput = true;
@@ -3391,12 +3370,13 @@
{
// remove output routes associated with this uid
SortedVector<routing_strategy> affectedStrategies;
- for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--) {
- sp<SessionRoute> route = mOutputRoutes.valueAt(i);
- if (route->mUid == uid) {
- mOutputRoutes.removeItemsAt(i);
- if (route->mDeviceDescriptor != 0) {
- affectedStrategies.add(getStrategy(route->mStreamType));
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ TrackClientMap clients = outputDesc->clientsMap();
+ for (const auto& client : clients) {
+ if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+ client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ affectedStrategies.add(getStrategy(client.second->stream()));
}
}
}
@@ -3407,12 +3387,13 @@
// remove input routes associated with this uid
SortedVector<audio_source_t> affectedSources;
- for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--) {
- sp<SessionRoute> route = mInputRoutes.valueAt(i);
- if (route->mUid == uid) {
- mInputRoutes.removeItemsAt(i);
- if (route->mDeviceDescriptor != 0) {
- affectedSources.add(route->mSource);
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
+ RecordClientMap clients = inputDesc->clientsMap();
+ for (const auto& client : clients) {
+ if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+ client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ affectedSources.add(client.second->source());
}
}
}
@@ -3486,7 +3467,8 @@
sp<SourceClientDescriptor> sourceDesc =
new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc,
- streamTypefromAttributesInt(attributes));
+ streamTypefromAttributesInt(attributes),
+ getStrategyForAttr(attributes));
status_t status = connectAudioSource(sourceDesc);
if (status == NO_ERROR) {
@@ -3503,7 +3485,7 @@
disconnectAudioSource(sourceDesc);
audio_attributes_t attributes = sourceDesc->attributes();
- routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
+ routing_strategy strategy = getStrategyForAttr(&attributes);
audio_stream_type_t stream = sourceDesc->stream();
sp<DeviceDescriptor> srcDeviceDesc = sourceDesc->srcDevice();
@@ -3557,7 +3539,7 @@
return INVALID_OPERATION;
}
uint32_t delayMs = 0;
- status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs);
+ status = startSource(outputDesc, sourceDesc, &delayMs);
if (status != NO_ERROR) {
mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0);
@@ -3931,10 +3913,9 @@
}
removeAudioPatch(sourceDesc->patchDesc()->mHandle);
- audio_stream_type_t stream = sourceDesc->stream();
sp<SwAudioOutputDescriptor> swOutputDesc = sourceDesc->swOutput().promote();
if (swOutputDesc != 0) {
- status_t status = stopSource(swOutputDesc, stream, false);
+ status_t status = stopSource(swOutputDesc, sourceDesc);
if (status == NO_ERROR) {
swOutputDesc->stop();
}
@@ -3958,8 +3939,7 @@
for (size_t i = 0; i < mAudioSources.size(); i++) {
sp<SourceClientDescriptor> sourceDesc = mAudioSources.valueAt(i);
audio_attributes_t attributes = sourceDesc->attributes();
- routing_strategy sourceStrategy =
- (routing_strategy) getStrategyForAttr(&attributes);
+ routing_strategy sourceStrategy = getStrategyForAttr(&attributes);
sp<SwAudioOutputDescriptor> outputDesc = sourceDesc->swOutput().promote();
if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) {
source = sourceDesc;
@@ -4936,11 +4916,41 @@
}
}
+template <class IoDescriptor, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+ IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices)
+{
+ auto activeClients = desc->clientsList(true /*activeOnly*/);
+ auto activeClientsWithRoute =
+ desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
+ active = activeClients.size() > 0;
+ if (active && activeClients.size() == activeClientsWithRoute.size()) {
+ return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId());
+ }
+ return nullptr;
+}
+
+template <class IoCollection, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+ IoCollection& ioCollection, Filter filter, const DeviceVector& devices)
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < ioCollection.size(); i++) {
+ auto desc = ioCollection.valueAt(i);
+ bool active;
+ sp<DeviceDescriptor> curDevice = findPreferredDevice(desc, filter, active, devices);
+ if (active && curDevice == nullptr) {
+ return nullptr;
+ } else if (curDevice != nullptr) {
+ device = curDevice;
+ }
+ }
+ return device;
+}
+
audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
bool fromCache)
{
- audio_devices_t device = AUDIO_DEVICE_NONE;
-
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -4951,18 +4961,13 @@
}
}
- // Check if an explicit routing request exists for an active stream on this output and
- // use it in priority before any other rule
- for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
- if (outputDesc->isStreamActive((audio_stream_type_t)stream)) {
- audio_devices_t forcedDevice =
- mOutputRoutes.getActiveDeviceForStream(
- (audio_stream_type_t)stream, mAvailableOutputDevices);
-
- if (forcedDevice != AUDIO_DEVICE_NONE) {
- return forcedDevice;
- }
- }
+ // Honor explicit routing requests only if no client using default routing is active on this
+ // input: a specific app can not force routing for other apps by setting a preferred device.
+ bool active; // unused
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
}
// check the following by order of priority to request a routing change if necessary:
@@ -4988,6 +4993,7 @@
// FIXME: extend use of isStrategyActiveOnSameModule() to all strategies
// with a refined rule considering mutually exclusive devices (using same backend)
// as opposed to all streams on the same audio HAL module.
+ audio_devices_t device = AUDIO_DEVICE_NONE;
if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE) &&
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
@@ -5030,6 +5036,15 @@
}
}
+ // Honor explicit routing requests only if no client using default routing is active on this
+ // input: a specific app can not force routing for other apps by setting a preferred device.
+ bool active;
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
+ }
+
// If we are not in call and no client is active on this input, this methods returns
// AUDIO_DEVICE_NONE, causing the patch on the input stream to be released.
audio_source_t source = inputDesc->getHighestPrioritySource(true /*activeOnly*/);
@@ -5091,16 +5106,16 @@
return mEngine->getStrategyForStream(stream);
}
-uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
+routing_strategy AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
// flags to strategy mapping
if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
- return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
+ return STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
}
if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
- return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
+ return STRATEGY_ENFORCED_AUDIBLE;
}
// usage to strategy mapping
- return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage));
+ return mEngine->getStrategyForUsage(attr->usage);
}
void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) {
@@ -5176,17 +5191,11 @@
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
- // Check if an explicit routing request exists for a stream type corresponding to the
- // specified strategy and use it in priority over default routing rules.
- for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
- if (getStrategy((audio_stream_type_t)stream) == strategy) {
- audio_devices_t forcedDevice =
- mOutputRoutes.getActiveDeviceForStream(
- (audio_stream_type_t)stream, mAvailableOutputDevices);
- if (forcedDevice != AUDIO_DEVICE_NONE) {
- return forcedDevice;
- }
- }
+ // Honor explicit routing requests only if all active clients have a preferred route in which
+ // case the last active client route is used
+ sp<DeviceDescriptor> deviceDesc = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
}
if (fromCache) {
@@ -5530,6 +5539,15 @@
audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
AudioMix **policyMix)
{
+ // Honor explicit routing requests only if all active clients have a preferred route in which
+ // case the last active client route is used
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(mInputs, inputSource, mAvailableInputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
+ }
+
+
audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
audio_devices_t selectedDeviceFromMix =
mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);
@@ -5542,20 +5560,7 @@
audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
{
- // Routing
- // Scan the whole RouteMap to see if we have an explicit route:
- // if the input source in the RouteMap is the same as the argument above,
- // and activity count is non-zero and the device in the route descriptor is available
- // then select this device.
- for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
- sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
- if ((inputSource == route->mSource) && route->isActiveOrChanged() &&
- (mAvailableInputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
- return route->mDeviceDescriptor->type();
- }
- }
-
- return mEngine->getDeviceForInputSource(inputSource);
+ return mEngine->getDeviceForInputSource(inputSource);
}
float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
@@ -5860,7 +5865,7 @@
}
for (int i = 0; i < (int)AUDIO_STREAM_FOR_POLICY_CNT; i++) {
if (((getStrategy((audio_stream_type_t)i) == strategy) ||
- (NUM_STRATEGIES == strategy)) &&
+ (STRATEGY_NONE == strategy)) &&
outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) {
return true;
}