Merge "audiopolicy: switch to VolumeGroup for Output"

gugelfrei
Eric Laurent 5 years ago committed by Android (Google) Code Review
commit b3527ddc7e

@ -16,19 +16,22 @@
#pragma once #pragma once
#include <media/AudioCommonTypes.h>
#include <system/audio.h> #include <system/audio.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <math.h> #include <math.h>
namespace android { namespace android {
/** /**
* VolumeSource is the discriminent for volume management on an output. * VolumeSource is the discriminent for volume management on an output.
* It used to be the stream type by legacy, it may be host volume group or a volume curves if * It used to be the stream type by legacy, it may be host volume group or a volume curves if
* we allow to have more than one curve per volume group. * we allow to have more than one curve per volume group (mandatory to get rid of AudioServer
* stream aliases.
*/ */
enum VolumeSource : std::underlying_type<audio_stream_type_t>::type; enum VolumeSource : std::underlying_type<volume_group_t>::type;
static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(AUDIO_STREAM_DEFAULT); static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(VOLUME_GROUP_NONE);
static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) { static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) {
return static_cast<VolumeSource>(stream); return static_cast<VolumeSource>(stream);

@ -107,7 +107,7 @@ private:
}; };
/** /**
* Note: volume activities shall be indexed by CurvesId if we want to allow multiple * Note: volume activities shall be indexed by CurvesId if we want to allow multiple
* curves per volume group, inferring a mute management or volume balancing between HW and SW is * curves per volume source, inferring a mute management or volume balancing between HW and SW is
* done * done
*/ */
using VolumeActivities = std::map<VolumeSource, VolumeActivity>; using VolumeActivities = std::map<VolumeSource, VolumeActivity>;
@ -157,7 +157,7 @@ public:
virtual uint32_t latency() { return 0; } virtual uint32_t latency() { return 0; }
virtual bool isFixedVolume(audio_devices_t device); virtual bool isFixedVolume(audio_devices_t device);
virtual bool setVolume(float volumeDb, virtual bool setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource volumeSource, const StreamTypeVector &streams,
audio_devices_t device, audio_devices_t device,
uint32_t delayMs, uint32_t delayMs,
bool force); bool force);
@ -221,7 +221,7 @@ public:
} }
void setCurVolume(VolumeSource vs, float volumeDb) void setCurVolume(VolumeSource vs, float volumeDb)
{ {
// Even if not activity for this group registered, need to create anyway // Even if not activity for this source registered, need to create anyway
mVolumeActivities[vs].setVolume(volumeDb); mVolumeActivities[vs].setVolume(volumeDb);
} }
float getCurVolume(VolumeSource vs) const float getCurVolume(VolumeSource vs) const
@ -280,6 +280,11 @@ public:
return mActiveClients; return mActiveClients;
} }
bool useHwGain() const
{
return !devices().isEmpty() ? devices().itemAt(0)->hasGainController() : false;
}
DeviceVector mDevices; /**< current devices this output is routed to */ DeviceVector mDevices; /**< current devices this output is routed to */
wp<AudioPolicyMix> mPolicyMix; // non NULL when used by a dynamic policy wp<AudioPolicyMix> mPolicyMix; // non NULL when used by a dynamic policy
@ -328,7 +333,7 @@ public:
} }
} }
virtual bool setVolume(float volumeDb, virtual bool setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource volumeSource, const StreamTypeVector &streams,
audio_devices_t device, audio_devices_t device,
uint32_t delayMs, uint32_t delayMs,
bool force); bool force);
@ -402,7 +407,7 @@ public:
void dump(String8 *dst) const override; void dump(String8 *dst) const override;
virtual bool setVolume(float volumeDb, virtual bool setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource volumeSource, const StreamTypeVector &streams,
audio_devices_t device, audio_devices_t device,
uint32_t delayMs, uint32_t delayMs,
bool force); bool force);
@ -422,7 +427,7 @@ public:
bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const; bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
/** /**
* return whether any source contributing to VolumeSource is playing remotely, override * return whether any source contributing to VolumeSource is playing remotely, override
* to change the definition of * to change the definition of
* local/remote playback, used for instance by notification manager to not make * local/remote playback, used for instance by notification manager to not make
* media players lose audio focus when not playing locally * media players lose audio focus when not playing locally
@ -488,8 +493,8 @@ public:
/** /**
* @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
* hold the volume source to be ignored * hold the volume source to be ignored
* @param volumeSourceToIgnore source not considered in the activity detection * @param volumeSourceToIgnore source not to be considered in the activity detection
* @return true if any output is active for any source except the one to be ignored * @return true if any output is active for any volume source except the one to be ignored
*/ */
bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
{ {
@ -518,8 +523,8 @@ public:
/** /**
* @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
* hold the volume source to be ignored * hold the volume source to be ignored
* @param volumeSourceToIgnore source not considered in the activity detection * @param volumeSourceToIgnore source not to be considered in the activity detection
* @return true if any output is active for any source except the one to be ignored * @return true if any output is active for any volume source except the one to be ignored
*/ */
bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
{ {

@ -24,6 +24,7 @@
#include "AudioGain.h" #include "AudioGain.h"
#include "Volume.h" #include "Volume.h"
#include "HwModule.h" #include "HwModule.h"
#include "TypeConverter.h"
#include <media/AudioParameter.h> #include <media/AudioParameter.h>
#include <media/AudioPolicy.h> #include <media/AudioPolicy.h>
@ -152,17 +153,18 @@ bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused)
} }
bool AudioOutputDescriptor::setVolume(float volumeDb, bool AudioOutputDescriptor::setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource volumeSource,
audio_devices_t device __unused, const StreamTypeVector &/*streams*/,
audio_devices_t /*device*/,
uint32_t delayMs, uint32_t delayMs,
bool force) bool force)
{ {
// We actually change the volume if: // We actually change the volume if:
// - the float value returned by computeVolume() changed // - the float value returned by computeVolume() changed
// - the force flag is set // - the force flag is set
if (volumeDb != getCurVolume(static_cast<VolumeSource>(stream)) || force) { if (volumeDb != getCurVolume(volumeSource) || force) {
ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volumeDb, delayMs); ALOGV("%s for volumeSrc %d, volume %f, delay %d", __func__, volumeSource, volumeDb, delayMs);
setCurVolume(static_cast<VolumeSource>(stream), volumeDb); setCurVolume(volumeSource, volumeDb);
return true; return true;
} }
return false; return false;
@ -391,23 +393,33 @@ void SwAudioOutputDescriptor::toAudioPort(
} }
bool SwAudioOutputDescriptor::setVolume(float volumeDb, bool SwAudioOutputDescriptor::setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource vs, const StreamTypeVector &streamTypes,
audio_devices_t device, audio_devices_t device,
uint32_t delayMs, uint32_t delayMs,
bool force) bool force)
{ {
if (!AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force)) { StreamTypeVector streams = streamTypes;
if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) {
return false; return false;
} }
if (streams.empty()) {
streams.push_back(AUDIO_STREAM_MUSIC);
}
if (!devices().isEmpty()) { if (!devices().isEmpty()) {
// Assume first device to check upon Gain Crontroller availability // Assume first device to check upon Gain Crontroller availability
// APM loops on all group, so filter on active group to set the port gain,
// let the other groups set the stream volume as per legacy
const auto &devicePort = devices().itemAt(0); const auto &devicePort = devices().itemAt(0);
ALOGV("%s: device %s hasGC %d", __FUNCTION__, if (devicePort->hasGainController(true) && isActive(vs)) {
devicePort->toString().c_str(), devices().itemAt(0)->hasGainController(true)); ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
if (devicePort->hasGainController(true)) { // @todo: here we might be in trouble if the SwOutput has several active clients with
// different Volume Source (or if we allow several curves within same volume group)
//
// @todo: default stream volume to max (0) when using HW Port gain? // @todo: default stream volume to max (0) when using HW Port gain?
float volumeAmpl = Volume::DbToAmpl(0); float volumeAmpl = Volume::DbToAmpl(0);
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); for (const auto &stream : streams) {
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
AudioGains gains = devicePort->getGains(); AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb(); int gainMinValueInMb = gains[0]->getMinValueInMb();
@ -424,11 +436,15 @@ bool SwAudioOutputDescriptor::setVolume(float volumeDb,
} }
} }
// Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled
float volumeAmpl = Volume::DbToAmpl(getCurVolume(static_cast<VolumeSource>(stream))); float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs); mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
} }
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); for (const auto &stream : streams) {
ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
return true; return true;
} }
@ -618,12 +634,13 @@ void HwAudioOutputDescriptor::toAudioPort(
bool HwAudioOutputDescriptor::setVolume(float volumeDb, bool HwAudioOutputDescriptor::setVolume(float volumeDb,
audio_stream_type_t stream, VolumeSource volumeSource, const StreamTypeVector &streams,
audio_devices_t device, audio_devices_t device,
uint32_t delayMs, uint32_t delayMs,
bool force) bool force)
{ {
bool changed = AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force); bool changed =
AudioOutputDescriptor::setVolume(volumeDb, volumeSource, streams, device, delayMs, force);
if (changed) { if (changed) {
// TODO: use gain controller on source device if any to adjust volume // TODO: use gain controller on source device if any to adjust volume

@ -84,10 +84,6 @@ public:
volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const override; volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const override;
StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const override;
AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const override;
status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override; status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override;
void dump(String8 *dst) const override; void dump(String8 *dst) const override;
@ -112,7 +108,7 @@ public:
VolumeSource toVolumeSource(audio_stream_type_t stream) const VolumeSource toVolumeSource(audio_stream_type_t stream) const
{ {
return static_cast<VolumeSource>(stream); return static_cast<VolumeSource>(getVolumeGroupForStreamType(stream));
} }
status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst);

@ -152,6 +152,8 @@ public:
volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const; volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const;
volume_group_t getDefaultVolumeGroup() const;
product_strategy_t getDefault() const; product_strategy_t getDefault() const;
void dump(String8 *dst, int spaces = 0) const; void dump(String8 *dst, int spaces = 0) const;

@ -218,6 +218,9 @@ VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t
VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const
{ {
volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream); volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream);
if (volGr == VOLUME_GROUP_NONE) {
volGr = mProductStrategies.getDefaultVolumeGroup();
}
const auto &iter = mVolumeGroups.find(volGr); const auto &iter = mVolumeGroups.find(volGr);
LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s", LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s",
toString(stream).c_str()); toString(stream).c_str());
@ -260,20 +263,6 @@ volume_group_t EngineBase::getVolumeGroupForStreamType(audio_stream_type_t strea
return mProductStrategies.getVolumeGroupForStreamType(stream); return mProductStrategies.getVolumeGroupForStreamType(stream);
} }
StreamTypeVector EngineBase::getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const
{
// @TODO default music stream to control volume if no group?
return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ?
mVolumeGroups.at(volumeGroup)->getStreamTypes() :
StreamTypeVector(AUDIO_STREAM_MUSIC);
}
AttributesVector EngineBase::getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const
{
return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ?
mVolumeGroups.at(volumeGroup)->getSupportedAttributes() : AttributesVector();
}
status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const
{ {
for (const auto &iter : mVolumeGroups) { for (const auto &iter : mVolumeGroups) {

@ -270,11 +270,7 @@ volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(const audio_attri
return group; return group;
} }
} }
product_strategy_t defaultStrategy = getDefault(); return getDefaultVolumeGroup();
if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
return VOLUME_GROUP_NONE;
}
return at(defaultStrategy)->getDefaultVolumeGroup();
} }
volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const
@ -285,6 +281,12 @@ volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type
return group; return group;
} }
} }
ALOGW("%s: no volume group for %s, using default", __func__, toString(stream).c_str());
return getDefaultVolumeGroup();
}
volume_group_t ProductStrategyMap::getDefaultVolumeGroup() const
{
product_strategy_t defaultStrategy = getDefault(); product_strategy_t defaultStrategy = getDefault();
if (defaultStrategy == PRODUCT_STRATEGY_NONE) { if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
return VOLUME_GROUP_NONE; return VOLUME_GROUP_NONE;

@ -283,10 +283,6 @@ public:
*/ */
virtual volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const = 0; virtual volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const = 0;
virtual StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const = 0;
virtual AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const = 0;
/** /**
* @brief listAudioVolumeGroups introspection API to get the Audio Volume Groups, aka * @brief listAudioVolumeGroups introspection API to get the Audio Volume Groups, aka
* former stream aliases in Audio Service, defining volume curves attached to one or more * former stream aliases in Audio Service, defining volume curves attached to one or more

@ -36,7 +36,9 @@ LOCAL_HEADER_LIBRARIES := \
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_STATIC_LIBRARIES := libpfw_utility LOCAL_STATIC_LIBRARIES := \
libpfw_utility \
libaudiopolicycomponents
LOCAL_MODULE_TAGS := optional LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libpolicy-subsystem LOCAL_MODULE := libpolicy-subsystem

@ -1085,7 +1085,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
sanitizedRequestedPortId, *stream, sanitizedRequestedPortId, *stream,
mEngine->getProductStrategyForAttributes(resultAttr), mEngine->getProductStrategyForAttributes(resultAttr),
streamToVolumeSource(*stream), toVolumeSource(resultAttr),
*flags, isRequestedDeviceForExclusiveUse, *flags, isRequestedDeviceForExclusiveUse,
std::move(weakSecondaryOutputDescs)); std::move(weakSecondaryOutputDescs));
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output); sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
@ -1694,8 +1694,9 @@ status_t AudioPolicyManager::startSource(const sp<SwAudioOutputDescriptor>& outp
setOutputDevices(outputDesc, devices, force, 0, NULL, requiresMuteCheck); setOutputDevices(outputDesc, devices, force, 0, NULL, requiresMuteCheck);
// apply volume rules for current stream and device if necessary // apply volume rules for current stream and device if necessary
checkAndSetVolume(stream, auto &curves = getVolumeCurves(client->attributes());
getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()), checkAndSetVolume(curves, client->volumeSource(),
curves.getVolumeIndex(outputDesc->devices().types()),
outputDesc, outputDesc,
outputDesc->devices().types()); outputDesc->devices().types());
@ -2387,107 +2388,27 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index, int index,
audio_devices_t device) audio_devices_t device)
{ {
auto &curves = getVolumeCurves(stream); auto attributes = mEngine->getAttributesForStreamType(stream);
// VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but auto volumeGroup = mEngine->getVolumeGroupForStreamType(stream);
// can be muted directly by an app that has MODIFY_PHONE_STATE permission. if (volumeGroup == VOLUME_GROUP_NONE) {
if (((index < curves.getVolumeIndexMin()) && ALOGE("%s: no group matching with stream %s", __FUNCTION__, toString(stream).c_str());
!((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
index == 0)) ||
(index > curves.getVolumeIndexMax())) {
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
return BAD_VALUE; return BAD_VALUE;
} }
ALOGV("%s: stream %s attributes=%s", __func__,
// Force max volume if stream cannot be muted toString(stream).c_str(), toString(attributes).c_str());
if (!curves.canBeMuted()) index = curves.getVolumeIndexMax(); return setVolumeGroupIndex(getVolumeCurves(stream), volumeGroup, index, device, attributes);
ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d",
stream, device, index);
// update other private stream volumes which follow this one
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
auto &curCurves = getVolumeCurves(static_cast<audio_stream_type_t>(curStream));
curCurves.addCurrentVolumeIndex(device, index);
}
// update volume on all outputs and streams matching the following:
// - The requested stream (or a stream matching for volume control) is active on the output
// - The device (or devices) selected by the engine for this stream includes
// the requested device
// - For non default requested device, currently selected device on the output is either the
// requested device or one of the devices selected by the engine for this stream
// - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
// no specific device volume value exists for currently selected device.
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
audio_devices_t curDevice = desc->devices().types();
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) {
continue;
}
if (!(desc->isActive(streamToVolumeSource((audio_stream_type_t)curStream)) || isInCall())) {
continue;
}
audio_devices_t curStreamDevice = Volume::getDeviceForVolume(
mEngine->getOutputDevicesForStream((audio_stream_type_t)curStream,
false /*fromCache*/).types());
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
((curStreamDevice & device) == 0)) {
continue;
}
bool applyVolume;
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curStreamDevice |= device;
applyVolume = (Volume::getDeviceForVolume(curDevice) & curStreamDevice) != 0;
} else {
applyVolume = !curves.hasVolumeIndexForDevice(curStreamDevice);
}
// rescale index before applying to curStream as ranges may be different for
// stream and curStream
int idx = rescaleVolumeIndex(index, stream, (audio_stream_type_t)curStream);
if (applyVolume) {
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
status_t volStatus = checkAndSetVolume(
(audio_stream_type_t)curStream, idx, desc, curDevice,
(stream == AUDIO_STREAM_SYSTEM) ?
TOUCH_SOUND_FIXED_DELAY_MS : 0);
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
}
return status;
} }
status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream,
int *index, int *index,
audio_devices_t device) audio_devices_t device)
{ {
if (index == NULL) {
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
return BAD_VALUE;
}
// if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device selected for this // if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device selected for this
// stream by the engine. // stream by the engine.
if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
device = mEngine->getOutputDevicesForStream(stream, true /*fromCache*/).types(); device = mEngine->getOutputDevicesForStream(stream, true /*fromCache*/).types();
} }
device = Volume::getDeviceForVolume(device); return getVolumeIndex(getVolumeCurves(stream), *index, device);
*index = getVolumeCurves(stream).getVolumeIndex(device);
ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
return NO_ERROR;
} }
status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr, status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr,
@ -2500,18 +2421,25 @@ status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_
ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str()); ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str());
return BAD_VALUE; return BAD_VALUE;
} }
ALOGD("%s: FOUND group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str()); ALOGV("%s: group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str());
return setVolumeGroupIndex(getVolumeCurves(attr), volumeGroup, index, device, attr); return setVolumeGroupIndex(getVolumeCurves(attr), volumeGroup, index, device, attr);
} }
status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group, status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group,
int index, int index,
audio_devices_t device, audio_devices_t device,
const audio_attributes_t /*attributes*/) const audio_attributes_t attributes)
{ {
ALOGVV("%s: group=%d", __func__, group); ALOGVV("%s: group=%d", __func__, group);
status_t status = NO_ERROR; status_t status = NO_ERROR;
setVolumeCurveIndex(group, index, device, curves); VolumeSource vs = toVolumeSource(group);
product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes);
status = setVolumeCurveIndex(index, device, curves);
if (status != NO_ERROR) {
ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device);
return status;
}
// update volume on all outputs and streams matching the following: // update volume on all outputs and streams matching the following:
// - The requested stream (or a stream matching for volume control) is active on the output // - The requested stream (or a stream matching for volume control) is active on the output
// - The device (or devices) selected by the engine for this stream includes // - The device (or devices) selected by the engine for this stream includes
@ -2520,21 +2448,116 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g
// requested device or one of the devices selected by the engine for this stream // requested device or one of the devices selected by the engine for this stream
// - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
// no specific device volume value exists for currently selected device. // no specific device volume value exists for currently selected device.
// @TODO for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
audio_devices_t curDevice = Volume::getDeviceForVolume(desc->devices().types());
// Inter / intra volume group priority management: Loop on strategies arranged by priority
// If a higher priority strategy is active, and the output is routed to a device with a
// HW Gain management, do not change the volume
bool applyVolume = false;
if (desc->useHwGain()) {
if (!(desc->isActive(group) || isInCall())) {
continue;
}
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
false /*preferredDevice*/);
if (activeClients.empty()) {
continue;
}
bool isPreempted = false;
bool isHigherPriority = productStrategy < strategy;
for (const auto &client : activeClients) {
if (isHigherPriority && (client->volumeSource() != vs)) {
ALOGV("%s: Strategy=%d (\nrequester:\n"
" group %d, volumeGroup=%d attributes=%s)\n"
" higher priority source active:\n"
" volumeGroup=%d attributes=%s) \n"
" on output %zu, bailing out", __func__, productStrategy,
group, group, toString(attributes).c_str(),
client->volumeSource(), toString(client->attributes()).c_str(), i);
applyVolume = false;
isPreempted = true;
break;
}
// However, continue for loop to ensure no higher prio clients running on output
if (client->volumeSource() == vs) {
applyVolume = true;
}
}
if (isPreempted || applyVolume) {
break;
}
}
if (!applyVolume) {
continue; // next output
}
status_t volStatus = checkAndSetVolume(curves, vs, index, desc, curDevice,
(vs == toVolumeSource(AUDIO_STREAM_SYSTEM)?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
continue;
}
for (auto curVolGroup : getVolumeGroups()) {
VolumeSource curVolSrc = toVolumeSource(curVolGroup);
if (!(curVolSrc == vs || isInCall())) {
continue;
}
if (!(desc->isActive(vs) || isInCall())) {
continue;
}
audio_devices_t curSrcDevice;
auto &curCurves = getVolumeCurves(curVolSrc);
auto curCurvAttrs = curCurves.getAttributes();
if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) {
auto attr = curCurvAttrs.front();
curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types();
} else if (!curCurves.getStreamTypes().empty()) {
auto stream = curCurves.getStreamTypes().front();
curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types();
} else {
ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, curVolSrc);
continue;
}
curSrcDevice = Volume::getDeviceForVolume(curSrcDevice);
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) {
continue;
}
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curSrcDevice |= device;
applyVolume = (curDevice & curSrcDevice) != 0;
} else {
applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
}
if (applyVolume) {
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
status_t volStatus = checkAndSetVolume(
curCurves, curVolSrc, index, desc, curDevice,
((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
}
mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/); mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
return status; return status;
} }
status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup, status_t AudioPolicyManager::setVolumeCurveIndex(int index,
int index,
audio_devices_t device, audio_devices_t device,
IVolumeCurves &volumeCurves) IVolumeCurves &volumeCurves)
{ {
// VOICE_CALL stream has minVolumeIndex > 0 but can be muted directly by an // VOICE_CALL stream has minVolumeIndex > 0 but can be muted directly by an
// app that has MODIFY_PHONE_STATE permission. // app that has MODIFY_PHONE_STATE permission.
// If voice is member of the volume group, it will contaminate all the member of this group bool hasVoice = hasVoiceStream(volumeCurves.getStreamTypes());
auto streams = mEngine->getStreamTypesForVolumeGroup(volumeGroup); if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoice && index == 0)) ||
if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoiceStream(streams) && index == 0)) ||
(index > volumeCurves.getVolumeIndexMax())) { (index > volumeCurves.getVolumeIndexMax())) {
ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index, ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index,
volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax()); volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax());
@ -2547,7 +2570,7 @@ status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup,
// Force max volume if stream cannot be muted // Force max volume if stream cannot be muted
if (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax(); if (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax();
ALOGD("%s device %08x, index %d", __FUNCTION__ , device, index); ALOGV("%s device %08x, index %d", __FUNCTION__ , device, index);
volumeCurves.addCurrentVolumeIndex(device, index); volumeCurves.addCurrentVolumeIndex(device, index);
return NO_ERROR; return NO_ERROR;
} }
@ -2709,19 +2732,12 @@ status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled)
bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{ {
bool active = false; return mOutputs.isActive(toVolumeSource(stream), inPastMs);
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT && !active; curStream++) {
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
active = mOutputs.isActive(streamToVolumeSource((audio_stream_type_t)curStream), inPastMs);
}
return active;
} }
bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
{ {
return mOutputs.isActiveRemotely(streamToVolumeSource((audio_stream_type_t)stream), inPastMs); return mOutputs.isActiveRemotely(toVolumeSource(stream), inPastMs);
} }
bool AudioPolicyManager::isSourceActive(audio_source_t source) const bool AudioPolicyManager::isSourceActive(audio_source_t source) const
@ -3750,11 +3766,11 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so
struct audio_patch dummyPatch = {}; struct audio_patch dummyPatch = {};
sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid); sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid);
sp<SourceClientDescriptor> sourceDesc = new SourceClientDescriptor( sp<SourceClientDescriptor> sourceDesc =
*portId, uid, *attributes, patchDesc, srcDevice, new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice,
mEngine->getStreamTypeForAttributes(*attributes), mEngine->getStreamTypeForAttributes(*attributes),
mEngine->getProductStrategyForAttributes(*attributes), mEngine->getProductStrategyForAttributes(*attributes),
streamToVolumeSource(mEngine->getStreamTypeForAttributes(*attributes))); toVolumeSource(*attributes));
status_t status = connectAudioSource(sourceDesc); status_t status = connectAudioSource(sourceDesc);
if (status == NO_ERROR) { if (status == NO_ERROR) {
@ -3919,7 +3935,7 @@ status_t AudioPolicyManager::getMasterMono(bool *mono)
float AudioPolicyManager::getStreamVolumeDB( float AudioPolicyManager::getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device) audio_stream_type_t stream, int index, audio_devices_t device)
{ {
return computeVolume(stream, index, device); return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index, device);
} }
status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats, status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats,
@ -5295,11 +5311,10 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) {
// mute/unmute AUDIO_STREAM_TTS on all outputs // mute/unmute AUDIO_STREAM_TTS on all outputs
ALOGV("\t muting %d", mute); ALOGV("\t muting %d", mute);
uint32_t maxLatency = 0; uint32_t maxLatency = 0;
auto ttsVolumeSource = toVolumeSource(AUDIO_STREAM_TTS);
for (size_t i = 0; i < mOutputs.size(); i++) { for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i); sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
setStreamMute(AUDIO_STREAM_TTS, mute/*on*/, setVolumeSourceMute(ttsVolumeSource, mute/*on*/, desc, 0 /*delay*/, AUDIO_DEVICE_NONE);
desc,
0 /*delay*/, AUDIO_DEVICE_NONE);
const uint32_t latency = desc->latency() * 2; const uint32_t latency = desc->latency() * 2;
if (latency > maxLatency) { if (latency > maxLatency) {
maxLatency = latency; maxLatency = latency;
@ -5383,15 +5398,12 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const sp<AudioOutputDescr
if (muteWaitMs < tempMuteWaitMs) { if (muteWaitMs < tempMuteWaitMs) {
muteWaitMs = tempMuteWaitMs; muteWaitMs = tempMuteWaitMs;
} }
for (const auto &activeVs : outputDesc->getActiveVolumeSources()) {
for (const auto &productStrategy : productStrategies) { // make sure that we do not start the temporary mute period too early in case of
if (outputDesc->isStrategyActive(productStrategy)) { // delayed device change
// make sure that we do not start the temporary mute period too early in case of setVolumeSourceMute(activeVs, true, outputDesc, delayMs);
// delayed device change setVolumeSourceMute(activeVs, false, outputDesc, delayMs + tempMuteDurationMs,
setStrategyMute(productStrategy, true, outputDesc, delayMs);
setStrategyMute(productStrategy, false, outputDesc, delayMs + tempMuteDurationMs,
devices.types()); devices.types());
}
} }
} }
@ -5615,51 +5627,51 @@ sp<IOProfile> AudioPolicyManager::getInputProfile(const sp<DeviceDescriptor> &de
return NULL; return NULL;
} }
float AudioPolicyManager::computeVolume(audio_stream_type_t stream, float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index, int index,
audio_devices_t device) audio_devices_t device)
{ {
auto &curves = getVolumeCurves(stream);
float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index); float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index);
// handle the case of accessibility active while a ringtone is playing: if the ringtone is much // handle the case of accessibility active while a ringtone is playing: if the ringtone is much
// louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch
// exploration of the dialer UI. In this situation, bring the accessibility volume closer to // exploration of the dialer UI. In this situation, bring the accessibility volume closer to
// the ringtone volume // the ringtone volume
if ((stream == AUDIO_STREAM_ACCESSIBILITY) const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
&& (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING);
&& isStreamActive(AUDIO_STREAM_RING, 0)) { const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC);
const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device); const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM);
return ringVolumeDB - 4 > volumeDb ? ringVolumeDB - 4 : volumeDb;
if (volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY)
&& (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) &&
mOutputs.isActive(ringVolumeSrc, 0)) {
auto &ringCurves = getVolumeCurves(AUDIO_STREAM_RING);
const float ringVolumeDb = computeVolume(ringCurves, ringVolumeSrc, index, device);
return ringVolumeDb - 4 > volumeDb ? ringVolumeDb - 4 : volumeDb;
} }
// in-call: always cap volume by voice volume + some low headroom // in-call: always cap volume by voice volume + some low headroom
if ((stream != AUDIO_STREAM_VOICE_CALL) && if ((volumeSource != callVolumeSrc && (isInCall() ||
(isInCall() || mOutputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL)))) { mOutputs.isActiveLocally(callVolumeSrc))) &&
switch (stream) { (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM) ||
case AUDIO_STREAM_SYSTEM: volumeSource == ringVolumeSrc || volumeSource == musicVolumeSrc ||
case AUDIO_STREAM_RING: volumeSource == alarmVolumeSrc ||
case AUDIO_STREAM_MUSIC: volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION) ||
case AUDIO_STREAM_ALARM: volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
case AUDIO_STREAM_NOTIFICATION: volumeSource == toVolumeSource(AUDIO_STREAM_DTMF) ||
case AUDIO_STREAM_ENFORCED_AUDIBLE: volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY))) {
case AUDIO_STREAM_DTMF: auto &voiceCurves = getVolumeCurves(callVolumeSrc);
case AUDIO_STREAM_ACCESSIBILITY: { int voiceVolumeIndex = voiceCurves.getVolumeIndex(device);
int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device); const float maxVoiceVolDb =
const float maxVoiceVolDb = computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device)
computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device)
+ IN_CALL_EARPIECE_HEADROOM_DB; + IN_CALL_EARPIECE_HEADROOM_DB;
if (volumeDb > maxVoiceVolDb) { if (volumeDb > maxVoiceVolDb) {
ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f", ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__,
stream, volumeDb, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb); volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb);
volumeDb = maxVoiceVolDb; volumeDb = maxVoiceVolDb;
}
} break;
default:
break;
} }
} }
// if a headset is connected, apply the following rules to ring tones and notifications // if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears: // to avoid sound level bursts in user's ears:
// - always attenuate notifications volume by 6dB // - always attenuate notifications volume by 6dB
@ -5667,19 +5679,17 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
// speaker is part of the select devices // speaker is part of the select devices
// - if music is playing, always limit the volume to current music volume, // - if music is playing, always limit the volume to current music volume,
// with a minimum threshold at -36dB so that notification is always perceived. // with a minimum threshold at -36dB so that notification is always perceived.
if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_USB_HEADSET | AUDIO_DEVICE_OUT_HEARING_AID)) &&
AUDIO_DEVICE_OUT_WIRED_HEADPHONE | ((volumeSource == alarmVolumeSrc ||
AUDIO_DEVICE_OUT_USB_HEADSET | volumeSource == ringVolumeSrc) ||
AUDIO_DEVICE_OUT_HEARING_AID)) && (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) ||
((stream == AUDIO_STREAM_ALARM || stream == AUDIO_STREAM_RING) (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) ||
|| (stream == AUDIO_STREAM_NOTIFICATION) ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
|| (stream == AUDIO_STREAM_SYSTEM) (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
|| ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && curves.canBeMuted()) {
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) ==
AUDIO_POLICY_FORCE_NONE))) &&
getVolumeCurves(stream).canBeMuted()) {
// when the phone is ringing we must consider that music could have been paused just before // when the phone is ringing we must consider that music could have been paused just before
// by the music application and behave as if music was active if the last music track was // by the music application and behave as if music was active if the last music track was
// just stopped // just stopped
@ -5689,29 +5699,29 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
audio_devices_t musicDevice = audio_devices_t musicDevice =
mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA),
nullptr, true /*fromCache*/).types(); nullptr, true /*fromCache*/).types();
float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, auto &musicCurves = getVolumeCurves(AUDIO_STREAM_MUSIC);
getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice), float musicVolDb = computeVolume(musicCurves, musicVolumeSrc,
musicDevice); musicCurves.getVolumeIndex(musicDevice), musicDevice);
float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB;
if (volumeDb > minVolDB) { if (volumeDb > minVolDb) {
volumeDb = minVolDB; volumeDb = minVolDb;
ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB); ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDb, musicVolDb);
} }
if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) {
// on A2DP, also ensure notification volume is not too low compared to media when // on A2DP, also ensure notification volume is not too low compared to media when
// intended to be played // intended to be played
if ((volumeDb > -96.0f) && if ((volumeDb > -96.0f) &&
(musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) { (musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) {
ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f", ALOGV("%s increasing volume for volume source=%d device=0x%X from %f to %f",
stream, device, __func__, volumeSource, device, volumeDb,
volumeDb, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB);
volumeDb = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; volumeDb = musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB;
} }
} }
} else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) || } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) ||
(stream != AUDIO_STREAM_ALARM && stream != AUDIO_STREAM_RING)) { (!(volumeSource == alarmVolumeSrc || volumeSource == ringVolumeSrc))) {
volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
} }
} }
@ -5745,58 +5755,61 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex,
return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc)); return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc));
} }
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index, int index,
const sp<AudioOutputDescriptor>& outputDesc, const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device, audio_devices_t device,
int delayMs, int delayMs,
bool force) bool force)
{ {
// do not change actual stream volume if the stream is muted // do not change actual attributes volume if the attributes is muted
if (outputDesc->isMuted(streamToVolumeSource(stream))) { if (outputDesc->isMuted(volumeSource)) {
ALOGVV("%s() stream %d muted count %d", __func__, stream, outputDesc->getMuteCount(stream)); ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR; return NO_ERROR;
} }
VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO);
bool isVoiceVolSrc = callVolSrc == volumeSource;
bool isBtScoVolSrc = btScoVolSrc == volumeSource;
audio_policy_forced_cfg_t forceUseForComm = audio_policy_forced_cfg_t forceUseForComm =
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION); mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
// do not change in call volume if bluetooth is connected and vice versa // do not change in call volume if bluetooth is connected and vice versa
if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) || // if sco and call follow same curves, bypass forceUseForComm
(stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) { if ((callVolSrc != btScoVolSrc) &&
ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", ((isVoiceVolSrc && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
stream, forceUseForComm); (isBtScoVolSrc && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO))) {
ALOGV("%s cannot set volume group %d volume with force use = %d for comm", __func__,
volumeSource, forceUseForComm);
return INVALID_OPERATION; return INVALID_OPERATION;
} }
if (device == AUDIO_DEVICE_NONE) { if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->devices().types(); device = outputDesc->devices().types();
} }
float volumeDb = computeVolume(stream, index, device); float volumeDb = computeVolume(curves, volumeSource, index, device);
if (outputDesc->isFixedVolume(device) || if (outputDesc->isFixedVolume(device) ||
// Force VoIP volume to max for bluetooth SCO // Force VoIP volume to max for bluetooth SCO
((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && ((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
(device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
volumeDb = 0.0f; volumeDb = 0.0f;
} }
outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force);
outputDesc->setVolume(volumeDb, stream, device, delayMs, force); if (isVoiceVolSrc || isBtScoVolSrc) {
if (stream == AUDIO_STREAM_VOICE_CALL ||
stream == AUDIO_STREAM_BLUETOOTH_SCO) {
float voiceVolume; float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset // Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AUDIO_STREAM_VOICE_CALL) { if (isVoiceVolSrc) {
voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax(); voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else { } else {
voiceVolume = 1.0; voiceVolume = 1.0;
} }
if (voiceVolume != mLastVoiceVolume) { if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs); mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume; mLastVoiceVolume = voiceVolume;
} }
} }
return NO_ERROR; return NO_ERROR;
} }
@ -5806,14 +5819,10 @@ void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& out
bool force) bool force)
{ {
ALOGVV("applyStreamVolumes() for device %08x", device); ALOGVV("applyStreamVolumes() for device %08x", device);
for (const auto &volumeGroup : mEngine->getVolumeGroups()) {
for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { auto &curves = getVolumeCurves(toVolumeSource(volumeGroup));
checkAndSetVolume((audio_stream_type_t)stream, checkAndSetVolume(curves, toVolumeSource(volumeGroup),
getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device), curves.getVolumeIndex(device), outputDesc, device, delayMs, force);
outputDesc,
device,
delayMs,
force);
} }
} }
@ -5823,43 +5832,54 @@ void AudioPolicyManager::setStrategyMute(product_strategy_t strategy,
int delayMs, int delayMs,
audio_devices_t device) audio_devices_t device)
{ {
for (auto stream: mEngine->getStreamTypesForProductStrategy(strategy)) { std::vector<VolumeSource> sourcesToMute;
ALOGVV("%s() stream %d, mute %d, output ID %d", __FUNCTION__, stream, on, for (auto attributes: mEngine->getAllAttributesForProductStrategy(strategy)) {
outputDesc->getId()); ALOGVV("%s() attributes %s, mute %d, output ID %d", __func__,
setStreamMute(stream, on, outputDesc, delayMs, device); toString(attributes).c_str(), on, outputDesc->getId());
VolumeSource source = toVolumeSource(attributes);
if (std::find(begin(sourcesToMute), end(sourcesToMute), source) == end(sourcesToMute)) {
sourcesToMute.push_back(source);
}
} }
for (auto source : sourcesToMute) {
setVolumeSourceMute(source, on, outputDesc, delayMs, device);
}
} }
void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, void AudioPolicyManager::setVolumeSourceMute(VolumeSource volumeSource,
bool on, bool on,
const sp<AudioOutputDescriptor>& outputDesc, const sp<AudioOutputDescriptor>& outputDesc,
int delayMs, int delayMs,
audio_devices_t device) audio_devices_t device,
bool activeOnly)
{ {
if (activeOnly && !outputDesc->isActive(volumeSource)) {
return;
}
if (device == AUDIO_DEVICE_NONE) { if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->devices().types(); device = outputDesc->devices().types();
} }
auto &curves = getVolumeCurves(volumeSource);
ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
stream, on, outputDesc->getMuteCount(stream), device);
auto &curves = getVolumeCurves(stream);
if (on) { if (on) {
if (!outputDesc->isMuted(streamToVolumeSource(stream))) { if (!outputDesc->isMuted(volumeSource)) {
if (curves.canBeMuted() && if (curves.canBeMuted() &&
((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || (volumeSource != toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) ==
checkAndSetVolume(stream, 0, outputDesc, device, delayMs); AUDIO_POLICY_FORCE_NONE))) {
checkAndSetVolume(curves, volumeSource, 0, outputDesc, device, delayMs);
} }
} }
// increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored // increment mMuteCount after calling checkAndSetVolume() so that volume change is not
outputDesc->incMuteCount(streamToVolumeSource(stream)); // ignored
outputDesc->incMuteCount(volumeSource);
} else { } else {
if (!outputDesc->isMuted(streamToVolumeSource(stream))) { if (!outputDesc->isMuted(volumeSource)) {
ALOGV("setStreamMute() unmuting non muted stream!"); ALOGV("%s unmuting non muted attributes!", __func__);
return; return;
} }
if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { if (outputDesc->decMuteCount(volumeSource) == 0) {
checkAndSetVolume(stream, checkAndSetVolume(curves, volumeSource,
curves.getVolumeIndex(device), curves.getVolumeIndex(device),
outputDesc, outputDesc,
device, device,

@ -174,8 +174,7 @@ public:
status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index, status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index,
audio_devices_t device, const audio_attributes_t attributes); audio_devices_t device, const audio_attributes_t attributes);
status_t setVolumeCurveIndex(volume_group_t volumeGroup, status_t setVolumeCurveIndex(int index,
int index,
audio_devices_t device, audio_devices_t device,
IVolumeCurves &volumeCurves); IVolumeCurves &volumeCurves);
@ -358,6 +357,30 @@ protected:
return mDefaultOutputDevice; return mDefaultOutputDevice;
} }
std::vector<volume_group_t> getVolumeGroups() const
{
return mEngine->getVolumeGroups();
}
VolumeSource toVolumeSource(volume_group_t volumeGroup) const
{
return static_cast<VolumeSource>(volumeGroup);
}
VolumeSource toVolumeSource(const audio_attributes_t &attributes) const
{
return toVolumeSource(mEngine->getVolumeGroupForAttributes(attributes));
}
VolumeSource toVolumeSource(audio_stream_type_t stream) const
{
return toVolumeSource(mEngine->getVolumeGroupForStreamType(stream));
}
IVolumeCurves &getVolumeCurves(VolumeSource volumeSource)
{
auto *curves = mEngine->getVolumeCurvesForVolumeGroup(
static_cast<volume_group_t>(volumeSource));
ALOG_ASSERT(curves != nullptr, "No curves for volume source %d", volumeSource);
return *curves;
}
IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr) IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr)
{ {
auto *curves = mEngine->getVolumeCurvesForAttributes(attr); auto *curves = mEngine->getVolumeCurvesForAttributes(attr);
@ -395,7 +418,8 @@ protected:
// compute the actual volume for a given stream according to the requested index and a particular // compute the actual volume for a given stream according to the requested index and a particular
// device // device
virtual float computeVolume(audio_stream_type_t stream, virtual float computeVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index, int index,
audio_devices_t device); audio_devices_t device);
@ -404,7 +428,8 @@ protected:
audio_stream_type_t srcStream, audio_stream_type_t srcStream,
audio_stream_type_t dstStream); audio_stream_type_t dstStream);
// check that volume change is permitted, compute and send new volume to audio hardware // check that volume change is permitted, compute and send new volume to audio hardware
virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index, virtual status_t checkAndSetVolume(IVolumeCurves &curves,
VolumeSource volumeSource, int index,
const sp<AudioOutputDescriptor>& outputDesc, const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device, audio_devices_t device,
int delayMs = 0, bool force = false); int delayMs = 0, bool force = false);
@ -428,12 +453,22 @@ protected:
int delayMs = 0, int delayMs = 0,
audio_devices_t device = AUDIO_DEVICE_NONE); audio_devices_t device = AUDIO_DEVICE_NONE);
// Mute or unmute the stream on the specified output /**
void setStreamMute(audio_stream_type_t stream, * @brief setVolumeSourceMute Mute or unmute the volume source on the specified output
bool on, * @param volumeSource to be muted/unmute (may host legacy streams or by extension set of
const sp<AudioOutputDescriptor>& outputDesc, * audio attributes)
int delayMs = 0, * @param on true to mute, false to umute
audio_devices_t device = (audio_devices_t)0); * @param outputDesc on which the client following the volume group shall be muted/umuted
* @param delayMs
* @param device
* @param activeOnly if true, mute only if the volume group is active on the output.
*/
void setVolumeSourceMute(VolumeSource volumeSource,
bool on,
const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_devices_t device = AUDIO_DEVICE_NONE,
bool activeOnly = false);
audio_mode_t getPhoneState(); audio_mode_t getPhoneState();

Loading…
Cancel
Save