audiopolicy: switch to VolumeGroup for Output

Volume used to be managed per stream type.
VolumeGroup is a configurable way to manage volume affinity.
This CL allows to switch to VolumeGroup as new volume affinity management.

Bug: 124767636
Test: audio smoke tests

Change-Id: I71fc214f6db3158f0f05920cc3d700b29db1a4bc
Signed-off-by: François Gaffie <francois.gaffie@renault.com>
diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h
index 48b5271..561f100 100644
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -16,19 +16,22 @@
 
 #pragma once
 
+#include <media/AudioCommonTypes.h>
 #include <system/audio.h>
 #include <utils/Log.h>
 #include <math.h>
 
 namespace android {
 
+
 /**
  * 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
- * 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;
-static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(AUDIO_STREAM_DEFAULT);
+enum VolumeSource : std::underlying_type<volume_group_t>::type;
+static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(VOLUME_GROUP_NONE);
 
 static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) {
     return static_cast<VolumeSource>(stream);
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 704f404..04fc3d0 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -107,7 +107,7 @@
 };
 /**
  * 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
  */
 using VolumeActivities = std::map<VolumeSource, VolumeActivity>;
@@ -157,7 +157,7 @@
     virtual uint32_t latency() { return 0; }
     virtual bool isFixedVolume(audio_devices_t device);
     virtual bool setVolume(float volumeDb,
-                           audio_stream_type_t stream,
+                           VolumeSource volumeSource, const StreamTypeVector &streams,
                            audio_devices_t device,
                            uint32_t delayMs,
                            bool force);
@@ -221,7 +221,7 @@
     }
     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);
     }
     float getCurVolume(VolumeSource vs) const
@@ -280,6 +280,11 @@
         return mActiveClients;
     }
 
+    bool useHwGain() const
+    {
+        return !devices().isEmpty() ? devices().itemAt(0)->hasGainController() : false;
+    }
+
     DeviceVector mDevices; /**< current devices this output is routed to */
     AudioMix *mPolicyMix = nullptr;              // non NULL when used by a dynamic policy
 
@@ -328,7 +333,7 @@
         }
     }
     virtual bool setVolume(float volumeDb,
-                           audio_stream_type_t stream,
+                           VolumeSource volumeSource, const StreamTypeVector &streams,
                            audio_devices_t device,
                            uint32_t delayMs,
                            bool force);
@@ -402,7 +407,7 @@
             void dump(String8 *dst) const override;
 
     virtual bool setVolume(float volumeDb,
-                           audio_stream_type_t stream,
+                           VolumeSource volumeSource, const StreamTypeVector &streams,
                            audio_devices_t device,
                            uint32_t delayMs,
                            bool force);
@@ -422,7 +427,7 @@
     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
      * local/remote playback, used for instance by notification manager to not make
      * media players lose audio focus when not playing locally
@@ -488,8 +493,8 @@
     /**
      * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
      * hold the volume source to be ignored
-     * @param volumeSourceToIgnore source not considered in the activity detection
-     * @return true if any output is active for any source except the one to be ignored
+     * @param volumeSourceToIgnore source not to be considered in the activity detection
+     * @return true if any output is active for any volume source except the one to be ignored
      */
     bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
     {
@@ -518,8 +523,8 @@
     /**
      * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
      * hold the volume source to be ignored
-     * @param volumeSourceToIgnore source not considered in the activity detection
-     * @return true if any output is active for any source except the one to be ignored
+     * @param volumeSourceToIgnore source not to be considered in the activity detection
+     * @return true if any output is active for any volume source except the one to be ignored
      */
     bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
     {
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index fd33649..4d32559 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -23,6 +23,7 @@
 #include "AudioGain.h"
 #include "Volume.h"
 #include "HwModule.h"
+#include "TypeConverter.h"
 #include <media/AudioParameter.h>
 #include <media/AudioPolicy.h>
 
@@ -150,17 +151,18 @@
 }
 
 bool AudioOutputDescriptor::setVolume(float volumeDb,
-                                      audio_stream_type_t stream,
-                                      audio_devices_t device __unused,
+                                      VolumeSource volumeSource,
+                                      const StreamTypeVector &/*streams*/,
+                                      audio_devices_t /*device*/,
                                       uint32_t delayMs,
                                       bool force)
 {
     // We actually change the volume if:
     // - the float value returned by computeVolume() changed
     // - the force flag is set
-    if (volumeDb != getCurVolume(static_cast<VolumeSource>(stream)) || force) {
-        ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volumeDb, delayMs);
-        setCurVolume(static_cast<VolumeSource>(stream), volumeDb);
+    if (volumeDb != getCurVolume(volumeSource) || force) {
+        ALOGV("%s for volumeSrc %d, volume %f, delay %d", __func__, volumeSource, volumeDb, delayMs);
+        setCurVolume(volumeSource, volumeDb);
         return true;
     }
     return false;
@@ -389,23 +391,33 @@
 }
 
 bool SwAudioOutputDescriptor::setVolume(float volumeDb,
-                                        audio_stream_type_t stream,
+                                        VolumeSource vs, const StreamTypeVector &streamTypes,
                                         audio_devices_t device,
                                         uint32_t delayMs,
                                         bool force)
 {
-    if (!AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force)) {
+    StreamTypeVector streams = streamTypes;
+    if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) {
         return false;
     }
+    if (streams.empty()) {
+        streams.push_back(AUDIO_STREAM_MUSIC);
+    }
     if (!devices().isEmpty()) {
         // 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);
-        ALOGV("%s: device %s hasGC %d", __FUNCTION__,
-            devicePort->toString().c_str(), devices().itemAt(0)->hasGainController(true));
-        if (devicePort->hasGainController(true)) {
+        if (devicePort->hasGainController(true) && isActive(vs)) {
+            ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
+            // @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?
             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();
             int gainMinValueInMb = gains[0]->getMinValueInMb();
@@ -422,11 +434,15 @@
         }
     }
     // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled
-    float volumeAmpl = Volume::DbToAmpl(getCurVolume(static_cast<VolumeSource>(stream)));
-    if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
+    float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
+    if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
         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;
 }
 
@@ -616,12 +632,13 @@
 
 
 bool HwAudioOutputDescriptor::setVolume(float volumeDb,
-                                        audio_stream_type_t stream,
+                                        VolumeSource volumeSource, const StreamTypeVector &streams,
                                         audio_devices_t device,
                                         uint32_t delayMs,
                                         bool force)
 {
-    bool changed = AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force);
+    bool changed =
+        AudioOutputDescriptor::setVolume(volumeDb, volumeSource, streams, device, delayMs, force);
 
     if (changed) {
       // TODO: use gain controller on source device if any to adjust volume
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 6ff8512..cedc78f 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -84,10 +84,6 @@
 
     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;
 
     void dump(String8 *dst) const override;
@@ -112,7 +108,7 @@
 
     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);
diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h
index 767a8ed..1a2a198 100644
--- a/services/audiopolicy/engine/common/include/ProductStrategy.h
+++ b/services/audiopolicy/engine/common/include/ProductStrategy.h
@@ -152,6 +152,8 @@
 
     volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const;
 
+    volume_group_t getDefaultVolumeGroup() const;
+
     product_strategy_t getDefault() const;
 
     void dump(String8 *dst, int spaces = 0) const;
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 4fe7b42..07a7e65 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -218,6 +218,9 @@
 VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const
 {
     volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream);
+    if (volGr == VOLUME_GROUP_NONE) {
+        volGr = mProductStrategies.getDefaultVolumeGroup();
+    }
     const auto &iter = mVolumeGroups.find(volGr);
     LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s",
                 toString(stream).c_str());
@@ -260,20 +263,6 @@
     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
 {
     for (const auto &iter : mVolumeGroups) {
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
index 16e6690..f74f190 100644
--- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -270,11 +270,7 @@
             return group;
         }
     }
-    product_strategy_t defaultStrategy = getDefault();
-    if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
-        return VOLUME_GROUP_NONE;
-    }
-    return at(defaultStrategy)->getDefaultVolumeGroup();
+    return getDefaultVolumeGroup();
 }
 
 volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const
@@ -285,6 +281,12 @@
             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();
     if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
         return VOLUME_GROUP_NONE;
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index 38f3401..4cf72c8 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -283,10 +283,6 @@
      */
     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
      * former stream aliases in Audio Service, defining volume curves attached to one or more
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk
index 65dc9af..4706d7d 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk
@@ -36,7 +36,9 @@
 
 LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
 
-LOCAL_STATIC_LIBRARIES := libpfw_utility
+LOCAL_STATIC_LIBRARIES := \
+    libpfw_utility \
+    libaudiopolicycomponents
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libpolicy-subsystem
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ea98253..494a68c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1084,7 +1084,7 @@
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                   sanitizedRequestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
-                                  streamToVolumeSource(*stream),
+                                  toVolumeSource(resultAttr),
                                   *flags, isRequestedDeviceForExclusiveUse,
                                   std::move(weakSecondaryOutputDescs));
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
@@ -1694,8 +1694,9 @@
                 setOutputDevices(outputDesc, devices, force, 0, NULL, requiresMuteCheck);
 
         // apply volume rules for current stream and device if necessary
-        checkAndSetVolume(stream,
-                          getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()),
+        auto &curves = getVolumeCurves(client->attributes());
+        checkAndSetVolume(curves, client->volumeSource(),
+                          curves.getVolumeIndex(outputDesc->devices().types()),
                           outputDesc,
                           outputDesc->devices().types());
 
@@ -2384,107 +2385,27 @@
                                                   int index,
                                                   audio_devices_t device)
 {
-    auto &curves = getVolumeCurves(stream);
-    // VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but
-    // can be muted directly by an app that has MODIFY_PHONE_STATE permission.
-    if (((index < curves.getVolumeIndexMin()) &&
-            !((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
-            index == 0)) ||
-            (index > curves.getVolumeIndexMax())) {
+    auto attributes = mEngine->getAttributesForStreamType(stream);
+    auto volumeGroup = mEngine->getVolumeGroupForStreamType(stream);
+    if (volumeGroup == VOLUME_GROUP_NONE) {
+        ALOGE("%s: no group matching with stream %s", __FUNCTION__, toString(stream).c_str());
         return BAD_VALUE;
     }
-    if (!audio_is_output_device(device)) {
-        return BAD_VALUE;
-    }
-
-    // Force max volume if stream cannot be muted
-    if (!curves.canBeMuted()) index = curves.getVolumeIndexMax();
-
-    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;
+    ALOGV("%s: stream %s attributes=%s", __func__,
+          toString(stream).c_str(), toString(attributes).c_str());
+    return setVolumeGroupIndex(getVolumeCurves(stream), volumeGroup, index, device, attributes);
 }
 
 status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream,
-                                                      int *index,
-                                                      audio_devices_t device)
+                                                  int *index,
+                                                  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
     // stream by the engine.
     if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
         device = mEngine->getOutputDevicesForStream(stream, true /*fromCache*/).types();
     }
-    device = Volume::getDeviceForVolume(device);
-
-    *index =  getVolumeCurves(stream).getVolumeIndex(device);
-    ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
-    return NO_ERROR;
+    return getVolumeIndex(getVolumeCurves(stream), *index, device);
 }
 
 status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr,
@@ -2497,18 +2418,25 @@
         ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str());
         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);
 }
 
 status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group,
                                                  int index,
                                                  audio_devices_t device,
-                                                 const audio_attributes_t /*attributes*/)
+                                                 const audio_attributes_t attributes)
 {
     ALOGVV("%s: group=%d", __func__, group);
     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:
     // - 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
@@ -2517,21 +2445,116 @@
     // 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.
-    // @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*/);
     return status;
 }
 
-status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup,
-                                                 int index,
+status_t AudioPolicyManager::setVolumeCurveIndex(int index,
                                                  audio_devices_t device,
                                                  IVolumeCurves &volumeCurves)
 {
     // VOICE_CALL stream has minVolumeIndex > 0  but can be muted directly by an
     // app that has MODIFY_PHONE_STATE permission.
-    // If voice is member of the volume group, it will contaminate all the member of this group
-    auto streams = mEngine->getStreamTypesForVolumeGroup(volumeGroup);
-    if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoiceStream(streams) && index == 0)) ||
+    bool hasVoice = hasVoiceStream(volumeCurves.getStreamTypes());
+    if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoice && index == 0)) ||
             (index > volumeCurves.getVolumeIndexMax())) {
         ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index,
               volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax());
@@ -2544,7 +2567,7 @@
     // Force max volume if stream cannot be muted
     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);
     return NO_ERROR;
 }
@@ -2706,19 +2729,12 @@
 
 bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
 {
-    bool active = false;
-    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;
+    return mOutputs.isActive(toVolumeSource(stream), inPastMs);
 }
 
 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
@@ -3747,11 +3763,11 @@
     struct audio_patch dummyPatch = {};
     sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid);
 
-    sp<SourceClientDescriptor> sourceDesc = new SourceClientDescriptor(
-                *portId, uid, *attributes, patchDesc, srcDevice,
-                mEngine->getStreamTypeForAttributes(*attributes),
-                mEngine->getProductStrategyForAttributes(*attributes),
-                streamToVolumeSource(mEngine->getStreamTypeForAttributes(*attributes)));
+    sp<SourceClientDescriptor> sourceDesc =
+        new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice,
+                                   mEngine->getStreamTypeForAttributes(*attributes),
+                                   mEngine->getProductStrategyForAttributes(*attributes),
+                                   toVolumeSource(*attributes));
 
     status_t status = connectAudioSource(sourceDesc);
     if (status == NO_ERROR) {
@@ -3916,7 +3932,7 @@
 float AudioPolicyManager::getStreamVolumeDB(
         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,
@@ -5292,11 +5308,10 @@
         // mute/unmute AUDIO_STREAM_TTS on all outputs
         ALOGV("\t muting %d", mute);
         uint32_t maxLatency = 0;
+        auto ttsVolumeSource = toVolumeSource(AUDIO_STREAM_TTS);
         for (size_t i = 0; i < mOutputs.size(); i++) {
             sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
-            setStreamMute(AUDIO_STREAM_TTS, mute/*on*/,
-                    desc,
-                    0 /*delay*/, AUDIO_DEVICE_NONE);
+            setVolumeSourceMute(ttsVolumeSource, mute/*on*/, desc, 0 /*delay*/, AUDIO_DEVICE_NONE);
             const uint32_t latency = desc->latency() * 2;
             if (latency > maxLatency) {
                 maxLatency = latency;
@@ -5380,15 +5395,12 @@
         if (muteWaitMs < tempMuteWaitMs) {
             muteWaitMs = tempMuteWaitMs;
         }
-
-        for (const auto &productStrategy : productStrategies) {
-            if (outputDesc->isStrategyActive(productStrategy)) {
-                // make sure that we do not start the temporary mute period too early in case of
-                // delayed device change
-                setStrategyMute(productStrategy, true, outputDesc, delayMs);
-                setStrategyMute(productStrategy, false, outputDesc, delayMs + tempMuteDurationMs,
+        for (const auto &activeVs : outputDesc->getActiveVolumeSources()) {
+            // make sure that we do not start the temporary mute period too early in case of
+            // delayed device change
+            setVolumeSourceMute(activeVs, true, outputDesc, delayMs);
+            setVolumeSourceMute(activeVs, false, outputDesc, delayMs + tempMuteDurationMs,
                                 devices.types());
-            }
         }
     }
 
@@ -5612,51 +5624,51 @@
     return NULL;
 }
 
-float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
+float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
+                                        VolumeSource volumeSource,
                                         int index,
                                         audio_devices_t device)
 {
-    auto &curves = getVolumeCurves(stream);
     float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index);
 
     // 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
     // exploration of the dialer UI. In this situation, bring the accessibility volume closer to
     // the ringtone volume
-    if ((stream == AUDIO_STREAM_ACCESSIBILITY)
-            && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState())
-            && isStreamActive(AUDIO_STREAM_RING, 0)) {
-        const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device);
-        return ringVolumeDB - 4 > volumeDb ? ringVolumeDB - 4 : volumeDb;
+    const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
+    const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING);
+    const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC);
+    const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM);
+
+    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
-    if ((stream != AUDIO_STREAM_VOICE_CALL) &&
-            (isInCall() || mOutputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
-        switch (stream) {
-        case AUDIO_STREAM_SYSTEM:
-        case AUDIO_STREAM_RING:
-        case AUDIO_STREAM_MUSIC:
-        case AUDIO_STREAM_ALARM:
-        case AUDIO_STREAM_NOTIFICATION:
-        case AUDIO_STREAM_ENFORCED_AUDIBLE:
-        case AUDIO_STREAM_DTMF:
-        case AUDIO_STREAM_ACCESSIBILITY: {
-            int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device);
-            const float maxVoiceVolDb =
-                computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device)
+    if ((volumeSource != callVolumeSrc && (isInCall() ||
+                                           mOutputs.isActiveLocally(callVolumeSrc))) &&
+            (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM) ||
+             volumeSource == ringVolumeSrc || volumeSource == musicVolumeSrc ||
+             volumeSource == alarmVolumeSrc ||
+             volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION) ||
+             volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
+             volumeSource == toVolumeSource(AUDIO_STREAM_DTMF) ||
+             volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY))) {
+        auto &voiceCurves = getVolumeCurves(callVolumeSrc);
+        int voiceVolumeIndex = voiceCurves.getVolumeIndex(device);
+        const float maxVoiceVolDb =
+                computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device)
                 + IN_CALL_EARPIECE_HEADROOM_DB;
-            if (volumeDb > maxVoiceVolDb) {
-                ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f",
-                        stream, volumeDb, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb);
-                volumeDb = maxVoiceVolDb;
-            }
-            } break;
-        default:
-            break;
+        if (volumeDb > maxVoiceVolDb) {
+            ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__,
+                  volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb);
+            volumeDb = maxVoiceVolDb;
         }
     }
-
     // if a headset is connected, apply the following rules to ring tones and notifications
     // to avoid sound level bursts in user's ears:
     // - always attenuate notifications volume by 6dB
@@ -5664,19 +5676,17 @@
     // speaker is part of the select devices
     // - if music is playing, always limit the volume to current music volume,
     // with a minimum threshold at -36dB so that notification is always perceived.
-    if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
-            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
-            AUDIO_DEVICE_OUT_WIRED_HEADSET |
-            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-            AUDIO_DEVICE_OUT_USB_HEADSET |
-            AUDIO_DEVICE_OUT_HEARING_AID)) &&
-        ((stream == AUDIO_STREAM_ALARM || stream == AUDIO_STREAM_RING)
-                || (stream == AUDIO_STREAM_NOTIFICATION)
-                || (stream == AUDIO_STREAM_SYSTEM)
-                || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) &&
-                    (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) ==
-                     AUDIO_POLICY_FORCE_NONE))) &&
-            getVolumeCurves(stream).canBeMuted()) {
+    if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                   AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+                   AUDIO_DEVICE_OUT_USB_HEADSET | AUDIO_DEVICE_OUT_HEARING_AID)) &&
+            ((volumeSource == alarmVolumeSrc ||
+              volumeSource == ringVolumeSrc) ||
+             (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) ||
+             (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) ||
+             ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
+              (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
+            curves.canBeMuted()) {
+
         // 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
         // just stopped
@@ -5686,29 +5696,29 @@
             audio_devices_t musicDevice =
                     mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA),
                                                            nullptr, true /*fromCache*/).types();
-            float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
-                                   getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice),
-                                   musicDevice);
-            float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
-                    musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB;
-            if (volumeDb > minVolDB) {
-                volumeDb = minVolDB;
-                ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB);
+            auto &musicCurves = getVolumeCurves(AUDIO_STREAM_MUSIC);
+            float musicVolDb = computeVolume(musicCurves, musicVolumeSrc,
+                                             musicCurves.getVolumeIndex(musicDevice), musicDevice);
+            float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
+                        musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB;
+            if (volumeDb > minVolDb) {
+                volumeDb = minVolDb;
+                ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDb, musicVolDb);
             }
             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
                 // intended to be played
                 if ((volumeDb > -96.0f) &&
-                        (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) {
-                    ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f",
-                            stream, device,
-                            volumeDb, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB);
-                    volumeDb = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB;
+                        (musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) {
+                    ALOGV("%s increasing volume for volume source=%d device=0x%X from %f to %f",
+                          __func__, volumeSource, device, 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) ||
-                (stream != AUDIO_STREAM_ALARM && stream != AUDIO_STREAM_RING)) {
+                   (!(volumeSource == alarmVolumeSrc || volumeSource == ringVolumeSrc))) {
             volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
         }
     }
@@ -5742,58 +5752,61 @@
     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,
                                                const sp<AudioOutputDescriptor>& outputDesc,
                                                audio_devices_t device,
                                                int delayMs,
                                                bool force)
 {
-    // do not change actual stream volume if the stream is muted
-    if (outputDesc->isMuted(streamToVolumeSource(stream))) {
-        ALOGVV("%s() stream %d muted count %d", __func__, stream, outputDesc->getMuteCount(stream));
+    // do not change actual attributes volume if the attributes is muted
+    if (outputDesc->isMuted(volumeSource)) {
+        ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
+               outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
         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 =
             mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
     // 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) ||
-        (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) {
-        ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
-             stream, forceUseForComm);
+    // if sco and call follow same curves, bypass forceUseForComm
+    if ((callVolSrc != btScoVolSrc) &&
+            ((isVoiceVolSrc && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
+             (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;
     }
-
     if (device == AUDIO_DEVICE_NONE) {
         device = outputDesc->devices().types();
     }
 
-    float volumeDb = computeVolume(stream, index, device);
+    float volumeDb = computeVolume(curves, volumeSource, index, device);
     if (outputDesc->isFixedVolume(device) ||
             // Force VoIP volume to max for bluetooth SCO
-            ((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
-             (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
+            ((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
         volumeDb = 0.0f;
     }
+    outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force);
 
-    outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
-
-    if (stream == AUDIO_STREAM_VOICE_CALL ||
-        stream == AUDIO_STREAM_BLUETOOTH_SCO) {
+    if (isVoiceVolSrc || isBtScoVolSrc) {
         float voiceVolume;
         // Force voice volume to max for bluetooth SCO as volume is managed by the headset
-        if (stream == AUDIO_STREAM_VOICE_CALL) {
-            voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax();
+        if (isVoiceVolSrc) {
+            voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
         } else {
             voiceVolume = 1.0;
         }
-
         if (voiceVolume != mLastVoiceVolume) {
             mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
             mLastVoiceVolume = voiceVolume;
         }
     }
-
     return NO_ERROR;
 }
 
@@ -5803,14 +5816,10 @@
                                                 bool force)
 {
     ALOGVV("applyStreamVolumes() for device %08x", device);
-
-    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
-        checkAndSetVolume((audio_stream_type_t)stream,
-                          getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device),
-                          outputDesc,
-                          device,
-                          delayMs,
-                          force);
+    for (const auto &volumeGroup : mEngine->getVolumeGroups()) {
+        auto &curves = getVolumeCurves(toVolumeSource(volumeGroup));
+        checkAndSetVolume(curves, toVolumeSource(volumeGroup),
+                          curves.getVolumeIndex(device), outputDesc, device, delayMs, force);
     }
 }
 
@@ -5820,43 +5829,54 @@
                                          int delayMs,
                                          audio_devices_t device)
 {
-    for (auto stream: mEngine->getStreamTypesForProductStrategy(strategy)) {
-        ALOGVV("%s() stream %d, mute %d, output ID %d", __FUNCTION__, stream, on,
-               outputDesc->getId());
-        setStreamMute(stream, on, outputDesc, delayMs, device);
+    std::vector<VolumeSource> sourcesToMute;
+    for (auto attributes: mEngine->getAllAttributesForProductStrategy(strategy)) {
+        ALOGVV("%s() attributes %s, mute %d, output ID %d", __func__,
+               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,
-                                           bool on,
-                                           const sp<AudioOutputDescriptor>& outputDesc,
-                                           int delayMs,
-                                           audio_devices_t device)
+void AudioPolicyManager::setVolumeSourceMute(VolumeSource volumeSource,
+                                             bool on,
+                                             const sp<AudioOutputDescriptor>& outputDesc,
+                                             int delayMs,
+                                             audio_devices_t device,
+                                             bool activeOnly)
 {
+    if (activeOnly && !outputDesc->isActive(volumeSource)) {
+        return;
+    }
     if (device == AUDIO_DEVICE_NONE) {
         device = outputDesc->devices().types();
     }
-
-    ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
-          stream, on, outputDesc->getMuteCount(stream), device);
-    auto &curves = getVolumeCurves(stream);
+    auto &curves = getVolumeCurves(volumeSource);
     if (on) {
-        if (!outputDesc->isMuted(streamToVolumeSource(stream))) {
+        if (!outputDesc->isMuted(volumeSource)) {
             if (curves.canBeMuted() &&
-                    ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
-                     (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
-                checkAndSetVolume(stream, 0, outputDesc, device, delayMs);
+                    (volumeSource != toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
+                     (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) ==
+                      AUDIO_POLICY_FORCE_NONE))) {
+                checkAndSetVolume(curves, volumeSource, 0, outputDesc, device, delayMs);
             }
         }
-        // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
-        outputDesc->incMuteCount(streamToVolumeSource(stream));
+        // increment mMuteCount after calling checkAndSetVolume() so that volume change is not
+        // ignored
+        outputDesc->incMuteCount(volumeSource);
     } else {
-        if (!outputDesc->isMuted(streamToVolumeSource(stream))) {
-            ALOGV("setStreamMute() unmuting non muted stream!");
+        if (!outputDesc->isMuted(volumeSource)) {
+            ALOGV("%s unmuting non muted attributes!", __func__);
             return;
         }
-        if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) {
-            checkAndSetVolume(stream,
+        if (outputDesc->decMuteCount(volumeSource) == 0) {
+            checkAndSetVolume(curves, volumeSource,
                               curves.getVolumeIndex(device),
                               outputDesc,
                               device,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 3a31e1e..2b65f45 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -174,8 +174,7 @@
         status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index,
                                      audio_devices_t device, const audio_attributes_t attributes);
 
-        status_t setVolumeCurveIndex(volume_group_t volumeGroup,
-                                     int index,
+        status_t setVolumeCurveIndex(int index,
                                      audio_devices_t device,
                                      IVolumeCurves &volumeCurves);
 
@@ -358,6 +357,30 @@
             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)
         {
             auto *curves = mEngine->getVolumeCurvesForAttributes(attr);
@@ -395,7 +418,8 @@
 
         // compute the actual volume for a given stream according to the requested index and a particular
         // device
-        virtual float computeVolume(audio_stream_type_t stream,
+        virtual float computeVolume(IVolumeCurves &curves,
+                                    VolumeSource volumeSource,
                                     int index,
                                     audio_devices_t device);
 
@@ -404,7 +428,8 @@
                                audio_stream_type_t srcStream,
                                audio_stream_type_t dstStream);
         // 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,
                                            audio_devices_t device,
                                            int delayMs = 0, bool force = false);
@@ -428,12 +453,22 @@
                              int delayMs = 0,
                              audio_devices_t device = AUDIO_DEVICE_NONE);
 
-        // Mute or unmute the stream on the specified output
-        void setStreamMute(audio_stream_type_t stream,
-                           bool on,
-                           const sp<AudioOutputDescriptor>& outputDesc,
-                           int delayMs = 0,
-                           audio_devices_t device = (audio_devices_t)0);
+        /**
+         * @brief setVolumeSourceMute Mute or unmute the volume source on the specified output
+         * @param volumeSource to be muted/unmute (may host legacy streams or by extension set of
+         * audio attributes)
+         * @param on true to mute, false to umute
+         * @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();