[BUG] AudioPolicyManager: fix volume on output using Hw Gain
2 clients following different volume group are connected to
a SwOutput. This output is routed to a device using Hw Gain.
The volume applied on the port if the one of the higher priority
strategy (order matters in the engine configuration file).
When the client with highest priority ends, the volume is not
reconsidered and is kept at the value of stopped output.
This CL forces to reapply the volume if SwOutput has still an
active client with different volume group and if routed device
is controling volume through Hw Gain.
Bug: 187173302
Test: build & boot & basic audio.
Signed-off-by: Francois Gaffie <francois.gaffie@renault.com>
Change-Id: I6f9795e1c70f1347e5d85b651da6ec272fda4083
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index b028e8f..00f5580 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2032,12 +2032,20 @@
if (outputDesc->getActivityCount(clientVolSrc) == 0 || forceDeviceUpdate) {
outputDesc->setStopTime(client, systemTime());
DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/);
+
+ // If the routing does not change, if an output is routed on a device using HwGain
+ // (aka setAudioPortConfig) and there are still active clients following different
+ // volume group(s), force reapply volume
+ bool requiresVolumeCheck = outputDesc->getActivityCount(clientVolSrc) == 0 &&
+ outputDesc->useHwGain() && outputDesc->isAnyActive(VOLUME_SOURCE_NONE);
+
// delay the device switch by twice the latency because stopOutput() is executed when
// the track stop() command is received and at that time the audio track buffer can
// still contain data that needs to be drained. The latency only covers the audio HAL
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
- setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2);
+ setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2,
+ nullptr, true /*requiresMuteCheck*/, requiresVolumeCheck);
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
@@ -6165,7 +6173,7 @@
bool force,
int delayMs,
audio_patch_handle_t *patchHandle,
- bool requiresMuteCheck)
+ bool requiresMuteCheck, bool requiresVolumeCheck)
{
ALOGV("%s device %s delayMs %d", __func__, devices.toString().c_str(), delayMs);
uint32_t muteWaitMs;
@@ -6181,6 +6189,7 @@
// filter devices according to output selected
DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
DeviceVector prevDevices = outputDesc->devices();
+ DeviceVector availPrevDevices = mAvailableOutputDevices.filter(prevDevices);
ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
@@ -6199,8 +6208,7 @@
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
// output profile or if new device is not supported AND previous device(s) is(are) still
// available (otherwise reset device must be done on the output)
- if (!devices.isEmpty() && filteredDevices.isEmpty() &&
- !mAvailableOutputDevices.filter(prevDevices).empty()) {
+ if (!devices.isEmpty() && filteredDevices.isEmpty() && !availPrevDevices.empty()) {
ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
// restore previous device after evaluating strategy mute state
outputDesc->setDevices(prevDevices);
@@ -6214,16 +6222,20 @@
// AND the output is connected by a valid audio patch.
// Doing this check here allows the caller to call setOutputDevices() without conditions
if ((filteredDevices.isEmpty() || filteredDevices == prevDevices) &&
- !force && outputDesc->getPatchHandle() != 0) {
+ !force && outputDesc->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) {
ALOGV("%s setting same device %s or null device, force=%d, patch handle=%d", __func__,
filteredDevices.toString().c_str(), force, outputDesc->getPatchHandle());
+ if (requiresVolumeCheck && !filteredDevices.isEmpty()) {
+ ALOGV("%s setting same device on routed output, force apply volumes", __func__);
+ applyStreamVolumes(outputDesc, filteredDevices.types(), delayMs, true /*force*/);
+ }
return muteWaitMs;
}
ALOGV("%s changing device to %s", __func__, filteredDevices.toString().c_str());
// do the routing
- if (filteredDevices.isEmpty()) {
+ if (filteredDevices.isEmpty() || mAvailableOutputDevices.filter(filteredDevices).empty()) {
resetOutputDevice(outputDesc, delayMs, NULL);
} else {
PatchBuilder patchBuilder;