audiopolicy: rework stream activity for volume management on output

-Manage duplicated output ref count in client itself
-Use VolumeSource to track volume activity in output
(today mapped on legacy stream type)
-Use accessor APIs to control activity/mute from apm.

Test: audio smoke tests
Bug: 124767636

Change-Id: I452e3973f6869d41231d984896a5886ebb86afb1
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 5ccc8fd..a3b6b36 100644
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -20,6 +20,22 @@
 #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.
+ */
+enum VolumeSource : std::underlying_type<audio_stream_type_t>::type;
+static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(AUDIO_STREAM_DEFAULT);
+
+static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) {
+    return static_cast<VolumeSource>(stream);
+}
+
+
+} // namespace android
+
 // Absolute min volume in dB (can be represented in single precision normal float value)
 #define VOLUME_MIN_DB (-758)
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index c84636e..cf9519b 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -73,7 +73,7 @@
 
     virtual void dump(String8 *dst, int spaces) const
     {
-        dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 " \n", spaces, "",
+        dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 ", ", spaces, "",
                           getActivityCount(), getStopTime());
     }
 private:
@@ -82,6 +82,37 @@
 };
 
 /**
+ * @brief VolumeActivity: it tracks the activity for volume policy (volume index, mute,
+ * memorize previous stop, and store mute if incompatible device with another strategy.
+ */
+class VolumeActivity : public ActivityTracking
+{
+public:
+    bool isMuted() const { return mMuteCount > 0; }
+    int getMuteCount() const { return mMuteCount; }
+    int incMuteCount() { return ++mMuteCount; }
+    int decMuteCount() { return mMuteCount > 0 ? --mMuteCount : -1; }
+
+    void dump(String8 *dst, int spaces) const override
+    {
+        ActivityTracking::dump(dst, spaces);
+        dst->appendFormat(", Volume: %.03f, MuteCount: %02d\n", mCurVolumeDb, mMuteCount);
+    }
+    void setVolume(float volume) { mCurVolumeDb = volume; }
+    float getVolume() const { return mCurVolumeDb; }
+
+private:
+    int mMuteCount = 0; /**< mute request counter */
+    float mCurVolumeDb = NAN; /**< current volume in dB. */
+};
+/**
+ * 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
+ * done
+ */
+using VolumeActivities = std::map<VolumeSource, VolumeActivity>;
+
+/**
  * @brief The Activity class: it tracks the activity for volume policy (volume index, mute,
  * memorize previous stop, and store mute if incompatible device with another strategy.
  * Having this class prevents from looping on all attributes (legacy streams) of the strategy
@@ -92,6 +123,10 @@
     void setMutedByDevice( bool isMuted) { mIsMutedByDevice = isMuted; }
     bool isMutedByDevice() const { return mIsMutedByDevice; }
 
+    void dump(String8 *dst, int spaces) const override {
+        ActivityTracking::dump(dst, spaces);
+        dst->appendFormat("\n");
+    }
 private:
     /**
      * strategies muted because of incompatible device selection.
@@ -128,15 +163,6 @@
                            bool force);
 
     /**
-     * Changes the stream active count and mActiveClients only.
-     * This does not change the client->active() state or the output descriptor's
-     * global active count.
-     */
-    virtual void changeStreamActiveCount(const sp<TrackClientDescriptor>& client, int delta);
-            uint32_t streamActiveCount(audio_stream_type_t stream) const
-                            { return mActiveCount[stream]; }
-
-    /**
      * @brief setStopTime set the stop time due to the client stoppage or a re routing of this
      * client
      * @param client to be considered
@@ -148,13 +174,61 @@
      * Changes the client->active() state and the output descriptor's global active count,
      * along with the stream active count and mActiveClients.
      * The client must be previously added by the base class addClient().
+     * In case of duplicating thread, client shall be added on the duplicated thread, not on the
+     * involved outputs but setClientActive will be called on all output to track strategy and
+     * active client for a given output.
+     * Active ref count of the client will be incremented/decremented through setActive API
      */
-            void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
+    virtual void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
 
-    bool isActive(uint32_t inPastMs = 0) const;
-    bool isStreamActive(audio_stream_type_t stream,
-                        uint32_t inPastMs = 0,
-                        nsecs_t sysTime = 0) const;
+    bool isActive(uint32_t inPastMs) const;
+    bool isActive(VolumeSource volumeSource = VOLUME_SOURCE_NONE,
+                  uint32_t inPastMs = 0,
+                  nsecs_t sysTime = 0) const;
+    bool isAnyActive(VolumeSource volumeSourceToIgnore) const;
+
+    std::vector<VolumeSource> getActiveVolumeSources() const {
+        std::vector<VolumeSource> activeList;
+        for (const auto &iter : mVolumeActivities) {
+            if (iter.second.isActive()) {
+                activeList.push_back(iter.first);
+            }
+        }
+        return activeList;
+    }
+    uint32_t getActivityCount(VolumeSource vs) const
+    {
+        return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
+                    mVolumeActivities.at(vs).getActivityCount() : 0;
+    }
+    bool isMuted(VolumeSource vs) const
+    {
+        return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
+                    mVolumeActivities.at(vs).isMuted() : false;
+    }
+    int getMuteCount(VolumeSource vs) const
+    {
+        return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
+                    mVolumeActivities.at(vs).getMuteCount() : 0;
+    }
+    int incMuteCount(VolumeSource vs)
+    {
+        return mVolumeActivities[vs].incMuteCount();
+    }
+    int decMuteCount(VolumeSource vs)
+    {
+        return mVolumeActivities[vs].decMuteCount();
+    }
+    void setCurVolume(VolumeSource vs, float volume)
+    {
+        // Even if not activity for this group registered, need to create anyway
+        mVolumeActivities[vs].setVolume(volume);
+    }
+    float getCurVolume(VolumeSource vs) const
+    {
+        return mVolumeActivities.find(vs) != std::end(mVolumeActivities) ?
+                    mVolumeActivities.at(vs).getVolume() : NAN;
+    }
 
     bool isStrategyActive(product_strategy_t ps, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const
     {
@@ -195,40 +269,36 @@
         // it is possible that when a client is removed, we could remove its
         // associated active count by calling changeStreamActiveCount(),
         // but that would be hiding a problem, so we log fatal instead.
-        auto it2 = mActiveClients.find(client);
-        LOG_ALWAYS_FATAL_IF(it2 != mActiveClients.end(),
-                "%s(%d) removing client portId %d which is active (count %zu)",
-                __func__, mId, portId, it2->second);
+        auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client);
+        LOG_ALWAYS_FATAL_IF(clientIter != mActiveClients.end(),
+                            "%s(%d) removing client portId %d which is active (count %d)",
+                            __func__, mId, portId, client->getActivityCount());
         ClientMapHandler<TrackClientDescriptor>::removeClient(portId);
     }
 
-    using ActiveClientMap = std::map<sp<TrackClientDescriptor>, size_t /* count */>;
-    // required for duplicating thread
-    const ActiveClientMap& getActiveClients() const {
+    const TrackClientVector& getActiveClients() const {
         return mActiveClients;
     }
 
     DeviceVector mDevices; /**< current devices this output is routed to */
-    nsecs_t mStopTime[AUDIO_STREAM_CNT];
-    int mMuteCount[AUDIO_STREAM_CNT];            // mute request counter
     AudioMix *mPolicyMix = nullptr;              // non NULL when used by a dynamic policy
 
 protected:
     const sp<AudioPort> mPort;
     AudioPolicyClientInterface * const mClientInterface;
-    float mCurVolume[AUDIO_STREAM_CNT];   // current stream volume in dB
-    uint32_t mActiveCount[AUDIO_STREAM_CNT]; // number of streams of each type active on this output
     uint32_t mGlobalActiveCount = 0;  // non-client-specific active count
     audio_patch_handle_t mPatchHandle = AUDIO_PATCH_HANDLE_NONE;
     audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE;
 
-    // The ActiveClientMap shows the clients that contribute to the streams counts
+    // The ActiveClients shows the clients that contribute to the @VolumeSource counts
     // and may include upstream clients from a duplicating thread.
     // Compare with the ClientMap (mClients) which are external AudioTrack clients of the
     // output descriptor (and do not count internal PatchTracks).
-    ActiveClientMap mActiveClients;
+    TrackClientVector mActiveClients;
 
     RoutingActivities mRoutingActivities; /**< track routing activity on this ouput.*/
+
+    VolumeActivities mVolumeActivities; /**< track volume activity on this ouput.*/
 };
 
 // Audio output driven by a software mixer in audio flinger.
@@ -250,8 +320,13 @@
     virtual bool isFixedVolume(audio_devices_t device);
     sp<SwAudioOutputDescriptor> subOutput1() { return mOutput1; }
     sp<SwAudioOutputDescriptor> subOutput2() { return mOutput2; }
-            void changeStreamActiveCount(
-                    const sp<TrackClientDescriptor>& client, int delta) override;
+    void setClientActive(const sp<TrackClientDescriptor>& client, bool active) override;
+    void setAllClientsInactive()
+    {
+        for (const auto &client : clientsList(true)) {
+            setClientActive(client, false);
+        }
+    }
     virtual bool setVolume(float volume,
                            audio_stream_type_t stream,
                            audio_devices_t device,
@@ -344,25 +419,27 @@
         public DefaultKeyedVector< audio_io_handle_t, sp<SwAudioOutputDescriptor> >
 {
 public:
-    bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+    bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
 
     /**
-     * return whether a stream is playing remotely, override to change the definition of
+     * 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
      * For the base implementation, "remotely" means playing during screen mirroring which
      * uses an output for playback with a non-empty, non "0" address.
      */
-    bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+    bool isActiveRemotely(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
 
     /**
-     * return whether a stream is playing, but not on a "remote" device.
+     * return whether any source contributing to VolumeSource is playing, but not on a "remote"
+     * device.
      * Override to change the definition of a local/remote playback.
      * Used for instance by policy manager to alter the speaker playback ("speaker safe" behavior)
      * when media plays or not locally.
      * For the base implementation, "remotely" means playing during screen mirroring.
      */
-    bool isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+    bool isActiveLocally(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
 
     /**
      * @brief isStrategyActiveOnSameModule checks if the given strategy is active (or was active
@@ -409,9 +486,21 @@
     sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
 
     /**
-     * return true if any output is playing anything besides the stream to ignore
+     * @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
      */
-    bool isAnyOutputActive(audio_stream_type_t streamToIgnore) const;
+    bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
+    {
+        for (size_t i = 0; i < size(); i++) {
+            const sp<AudioOutputDescriptor> &outputDesc = valueAt(i);
+            if (outputDesc->isAnyActive(volumeSourceToIgnore)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     audio_devices_t getSupportedDevices(audio_io_handle_t handle) const;
 
@@ -424,12 +513,24 @@
         public DefaultKeyedVector< audio_io_handle_t, sp<HwAudioOutputDescriptor> >
 {
 public:
-    bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+    bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
 
     /**
-     * return true if any output is playing anything besides the stream to ignore
+     * @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
      */
-    bool isAnyOutputActive(audio_stream_type_t streamToIgnore) const;
+    bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
+    {
+        for (size_t i = 0; i < size(); i++) {
+            const sp<AudioOutputDescriptor> &outputDesc = valueAt(i);
+            if (outputDesc->isAnyActive(volumeSourceToIgnore)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     void dump(String8 *dst) const;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 2e44a60..4bb225d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -28,6 +28,7 @@
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 #include <policy.h>
+#include <Volume.h>
 #include "AudioPatch.h"
 #include "EffectDescriptor.h"
 
@@ -62,7 +63,7 @@
         mPreferredDeviceId = preferredDeviceId;
     }
     bool isPreferredDeviceForExclusiveUse() const { return mPreferredDeviceForExclusiveUse; }
-    void setActive(bool active) { mActive = active; }
+    virtual void setActive(bool active) { mActive = active; }
     bool active() const { return mActive; }
     bool hasPreferredDevice(bool activeOnly = false) const {
         return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive);
@@ -85,12 +86,13 @@
     TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId,
                           audio_attributes_t attributes, audio_config_base_t config,
                           audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
-                          product_strategy_t strategy, audio_output_flags_t flags,
+                          product_strategy_t strategy, VolumeSource volumeSource,
+                          audio_output_flags_t flags,
                           bool isPreferredDeviceForExclusiveUse,
                           std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
                          isPreferredDeviceForExclusiveUse),
-        mStream(stream), mStrategy(strategy), mFlags(flags),
+        mStream(stream), mStrategy(strategy), mVolumeSource(volumeSource), mFlags(flags),
         mSecondaryOutputs(std::move(secondaryOutputs)) {}
     ~TrackClientDescriptor() override = default;
 
@@ -104,12 +106,41 @@
     const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
         return mSecondaryOutputs;
     };
+    VolumeSource volumeSource() const { return mVolumeSource; }
+
+    void setActive(bool active) override
+    {
+        int delta = active ? 1 : -1;
+        changeActivityCount(delta);
+    }
+    void changeActivityCount(int delta)
+    {
+        if (delta > 0) {
+            mActivityCount += delta;
+        } else {
+            LOG_ALWAYS_FATAL_IF(!mActivityCount, "%s(%s) invalid delta %d, inactive client",
+                                 __func__, toShortString().c_str(), delta);
+            LOG_ALWAYS_FATAL_IF(static_cast<int>(mActivityCount) < -delta,
+                                "%s(%s) invalid delta %d, active client count %d",
+                                 __func__, toShortString().c_str(), delta, mActivityCount);
+            mActivityCount += delta;
+        }
+        ClientDescriptor::setActive(mActivityCount > 0);
+    }
+    uint32_t getActivityCount() const { return mActivityCount; }
 
 private:
     const audio_stream_type_t mStream;
     const product_strategy_t mStrategy;
+    const VolumeSource mVolumeSource;
     const audio_output_flags_t mFlags;
     const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
+
+    /**
+     * required for duplicating thread, prevent from removing active client from an output
+     * involved in a duplication.
+     */
+    uint32_t mActivityCount = 0;
 };
 
 class RecordClientDescriptor: public ClientDescriptor
@@ -148,7 +179,8 @@
 public:
     SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes,
                            const sp<AudioPatch>& patchDesc, const sp<DeviceDescriptor>& srcDevice,
-                           audio_stream_type_t stream, product_strategy_t strategy);
+                           audio_stream_type_t stream, product_strategy_t strategy,
+                           VolumeSource volumeSource);
     ~SourceClientDescriptor() override = default;
 
     sp<AudioPatch> patchDesc() const { return mPatchDesc; }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 77e7add..7293bc4 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -34,16 +34,8 @@
 
 AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
                                              AudioPolicyClientInterface *clientInterface)
-    : mPort(port)
-    , mClientInterface(clientInterface)
+    : mPort(port), mClientInterface(clientInterface)
 {
-    // clear usage count for all stream types
-    for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
-        mActiveCount[i] = 0;
-        mCurVolume[i] = -1.0;
-        mMuteCount[i] = 0;
-        mStopTime[i] = 0;
-    }
     if (mPort.get() != nullptr) {
         mPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
         if (mPort->mGains.size() > 0) {
@@ -85,124 +77,73 @@
     return hasSameHwModuleAs(outputDesc);
 }
 
-void AudioOutputDescriptor::changeStreamActiveCount(const sp<TrackClientDescriptor>& client,
-                                                    int delta)
-{
-    if (delta == 0) return;
-    const audio_stream_type_t stream = client->stream();
-    if ((delta + (int)mActiveCount[stream]) < 0) {
-        // any mismatched active count will abort.
-        LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, active stream count %d",
-              __func__, client->toShortString().c_str(), delta, mActiveCount[stream]);
-        // mActiveCount[stream] = 0;
-        // return;
-    }
-    mActiveCount[stream] += delta;
-    mRoutingActivities[client->strategy()].changeActivityCount(delta);
-
-    if (delta > 0) {
-        mActiveClients[client] += delta;
-    } else {
-        auto it = mActiveClients.find(client);
-        if (it == mActiveClients.end()) { // client not found!
-            LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, inactive client",
-                    __func__, client->toShortString().c_str(), delta);
-        } else if (it->second < -delta) { // invalid delta!
-            LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, active client count %zu",
-                    __func__, client->toShortString().c_str(), delta, it->second);
-        }
-        it->second += delta;
-        if (it->second == 0) {
-            (void)mActiveClients.erase(it);
-        }
-    }
-
-    ALOGV("%s stream %d, count %d", __FUNCTION__, stream, mActiveCount[stream]);
-}
-
 void AudioOutputDescriptor::setStopTime(const sp<TrackClientDescriptor>& client, nsecs_t sysTime)
 {
-    mStopTime[client->stream()] = sysTime;
+    mVolumeActivities[client->volumeSource()].setStopTime(sysTime);
     mRoutingActivities[client->strategy()].setStopTime(sysTime);
 }
 
 void AudioOutputDescriptor::setClientActive(const sp<TrackClientDescriptor>& client, bool active)
 {
-    LOG_ALWAYS_FATAL_IF(getClient(client->portId()) == nullptr,
-        "%s(%d) does not exist on output descriptor", __func__, client->portId());
-
-    if (active == client->active()) {
-        ALOGW("%s(%s): ignored active: %d, current stream count %d",
-                __func__, client->toShortString().c_str(),
-                active, mActiveCount[client->stream()]);
+    auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client);
+    if (active == (clientIter != end(mActiveClients))) {
+        ALOGW("%s(%s): ignored active: %d, current stream count %d", __func__,
+              client->toShortString().c_str(), active,
+              mRoutingActivities.at(client->strategy()).getActivityCount());
         return;
     }
+    if (active) {
+        mActiveClients.push_back(client);
+    } else {
+        mActiveClients.erase(clientIter);
+    }
     const int delta = active ? 1 : -1;
-    changeStreamActiveCount(client, delta);
+    // If ps is unknown, it is time to track it!
+    mRoutingActivities[client->strategy()].changeActivityCount(delta);
+    mVolumeActivities[client->volumeSource()].changeActivityCount(delta);
 
     // Handle non-client-specific activity ref count
     int32_t oldGlobalActiveCount = mGlobalActiveCount;
     if (!active && mGlobalActiveCount < 1) {
         ALOGW("%s(%s): invalid deactivation with globalRefCount %d",
-                __func__, client->toShortString().c_str(), mGlobalActiveCount);
+              __func__, client->toShortString().c_str(), mGlobalActiveCount);
         mGlobalActiveCount = 1;
     }
     mGlobalActiveCount += delta;
 
-    if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) {
-        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
-        {
+    if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+        if ((oldGlobalActiveCount == 0) || (mGlobalActiveCount == 0)) {
             mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
-                                                            MIX_STATE_MIXING);
-        }
-    } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) {
-        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
-        {
-            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
-                                                            MIX_STATE_IDLE);
+                mGlobalActiveCount > 0 ? MIX_STATE_MIXING : MIX_STATE_IDLE);
         }
     }
-
     client->setActive(active);
 }
 
+bool AudioOutputDescriptor::isActive(VolumeSource vs, uint32_t inPastMs, nsecs_t sysTime) const
+{
+    return (vs == VOLUME_SOURCE_NONE) ?
+                isActive(inPastMs) : (mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
+                mVolumeActivities.at(vs).isActive(inPastMs, sysTime) : false);
+}
+
 bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
 {
     nsecs_t sysTime = 0;
     if (inPastMs != 0) {
         sysTime = systemTime();
     }
-    for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) {
-        if (i == AUDIO_STREAM_PATCH) {
+    for (const auto &iter : mVolumeActivities) {
+        if (iter.first == streamToVolumeSource(AUDIO_STREAM_PATCH)) {
             continue;
         }
-        if (isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) {
+        if (iter.second.isActive(inPastMs, sysTime)) {
             return true;
         }
     }
     return false;
 }
 
-bool AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream,
-                                           uint32_t inPastMs,
-                                           nsecs_t sysTime) const
-{
-    if (mActiveCount[stream] != 0) {
-        return true;
-    }
-    if (inPastMs == 0) {
-        return false;
-    }
-    if (sysTime == 0) {
-        sysTime = systemTime();
-    }
-    if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) {
-        return true;
-    }
-    return false;
-}
-
-
 bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused)
 {
     return false;
@@ -217,9 +158,9 @@
     // We actually change the volume if:
     // - the float value returned by computeVolume() changed
     // - the force flag is set
-    if (volume != mCurVolume[stream] || force) {
+    if (volume != getCurVolume(static_cast<VolumeSource>(stream)) || force) {
         ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
-        mCurVolume[stream] = volume;
+        setCurVolume(static_cast<VolumeSource>(stream), volume);
         return true;
     }
     return false;
@@ -266,6 +207,13 @@
     return clients;
 }
 
+bool AudioOutputDescriptor::isAnyActive(VolumeSource volumeSourceToIgnore) const
+{
+    return std::find_if(begin(mActiveClients), end(mActiveClients),
+                        [&volumeSourceToIgnore](const auto &client) {
+        return client->volumeSource() != volumeSourceToIgnore; }) != end(mActiveClients);
+}
+
 void AudioOutputDescriptor::dump(String8 *dst) const
 {
     dst->appendFormat(" ID: %d\n", mId);
@@ -274,20 +222,22 @@
     dst->appendFormat(" Channels: %08x\n", mChannelMask);
     dst->appendFormat(" Devices: %s\n", devices().toString().c_str());
     dst->appendFormat(" Global active count: %u\n", mGlobalActiveCount);
-    dst->append(" Stream volume activeCount muteCount\n");
-    for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) {
-        dst->appendFormat(" %02d     %.03f     %02d          %02d\n",
-                 i, mCurVolume[i], streamActiveCount((audio_stream_type_t)i), mMuteCount[i]);
+    for (const auto &iter : mRoutingActivities) {
+        dst->appendFormat(" Product Strategy id: %d", iter.first);
+        iter.second.dump(dst, 4);
+    }
+    for (const auto &iter : mVolumeActivities) {
+        dst->appendFormat(" Volume Activities id: %d", iter.first);
+        iter.second.dump(dst, 4);
     }
     dst->append(" AudioTrack Clients:\n");
     ClientMapHandler<TrackClientDescriptor>::dump(dst);
     dst->append("\n");
-    if (mActiveClients.size() > 0) {
+    if (!mActiveClients.empty()) {
         dst->append(" AudioTrack active (stream) clients:\n");
         size_t index = 0;
-        for (const auto& clientPair : mActiveClients) {
-            dst->appendFormat(" Refcount: %zu", clientPair.second);
-            clientPair.first->dump(dst, 2, index++);
+        for (const auto& client : mActiveClients) {
+            client->dump(dst, 2, index++);
         }
         dst->append(" \n");
     }
@@ -388,15 +338,14 @@
     }
 }
 
-void SwAudioOutputDescriptor::changeStreamActiveCount(const sp<TrackClientDescriptor>& client,
-                                                       int delta)
+void SwAudioOutputDescriptor::setClientActive(const sp<TrackClientDescriptor>& client, bool active)
 {
     // forward usage count change to attached outputs
     if (isDuplicated()) {
-        mOutput1->changeStreamActiveCount(client, delta);
-        mOutput2->changeStreamActiveCount(client, delta);
+        mOutput1->setClientActive(client, active);
+        mOutput2->setClientActive(client, active);
     }
-    AudioOutputDescriptor::changeStreamActiveCount(client, delta);
+    AudioOutputDescriptor::setClientActive(client, active);
 }
 
 bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
@@ -445,19 +394,16 @@
                                         uint32_t delayMs,
                                         bool force)
 {
-    bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);
-
-    if (changed) {
-        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
-        // enabled
-        float volume = Volume::DbToAmpl(mCurVolume[stream]);
-        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
-            mClientInterface->setStreamVolume(
-                    AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
-        }
-        mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
+    if (!AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force)) {
+        return false;
     }
-    return changed;
+    // 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) {
+        mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
+    }
+    mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+    return true;
 }
 
 status_t SwAudioOutputDescriptor::open(const audio_config_t *config,
@@ -660,24 +606,24 @@
 }
 
 // SwAudioOutputCollection implementation
-bool SwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+bool SwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < this->size(); i++) {
         const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
-        if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
+        if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
             return true;
         }
     }
     return false;
 }
 
-bool SwAudioOutputCollection::isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs) const
+bool SwAudioOutputCollection::isActiveLocally(VolumeSource volumeSource, uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < this->size(); i++) {
         const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
-        if (outputDesc->isStreamActive(stream, inPastMs, sysTime)
+        if (outputDesc->isActive(volumeSource, inPastMs, sysTime)
                 && ((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) == 0)) {
             return true;
         }
@@ -685,14 +631,13 @@
     return false;
 }
 
-bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
-                                                   uint32_t inPastMs) const
+bool SwAudioOutputCollection::isActiveRemotely(VolumeSource volumeSource, uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < size(); i++) {
         const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
         if (((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
-                outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
+                outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
             // do not consider re routing (when the output is going to a dynamic policy)
             // as "remote playback"
             if (outputDesc->mPolicyMix == NULL) {
@@ -775,22 +720,6 @@
     return NULL;
 }
 
-bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
-{
-    for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) {
-        if (s == (size_t) streamToIgnore) {
-            continue;
-        }
-        for (size_t i = 0; i < size(); i++) {
-            const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
-            if (outputDesc->streamActiveCount((audio_stream_type_t)s)!= 0) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getOutputForClient(audio_port_handle_t portId)
 {
     for (size_t i = 0; i < size(); i++) {
@@ -825,34 +754,18 @@
 }
 
 // HwAudioOutputCollection implementation
-bool HwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+bool HwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < this->size(); i++) {
         const sp<HwAudioOutputDescriptor> outputDesc = this->valueAt(i);
-        if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
+        if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
             return true;
         }
     }
     return false;
 }
 
-bool HwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
-{
-    for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) {
-        if (s == (size_t) streamToIgnore) {
-            continue;
-        }
-        for (size_t i = 0; i < size(); i++) {
-            const sp<HwAudioOutputDescriptor> outputDesc = valueAt(i);
-            if (outputDesc->streamActiveCount((audio_stream_type_t)s) != 0) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 void HwAudioOutputCollection::dump(String8 *dst) const
 {
     dst->append("\nOutputs dump:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 633c40e..ad07ab1 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -20,6 +20,7 @@
 #include <sstream>
 #include <utils/Log.h>
 #include <utils/String8.h>
+#include <TypeConverter.h>
 #include "AudioGain.h"
 #include "AudioOutputDescriptor.h"
 #include "AudioPatch.h"
@@ -45,6 +46,7 @@
              mPortId, mSessionId, mUid);
     dst->appendFormat("%*s- Format: %08x Sampling rate: %d Channels: %08x\n", spaces, "",
              mConfig.format, mConfig.sample_rate, mConfig.channel_mask);
+    dst->appendFormat("%*s- Attributes: %s\n", spaces, "", toString(mAttributes).c_str());
     dst->appendFormat("%*s- Preferred Device Id: %08x\n", spaces, "", mPreferredDeviceId);
     dst->appendFormat("%*s- State: %s\n", spaces, "", mActive ? "Active" : "Inactive");
 }
@@ -53,6 +55,7 @@
 {
     ClientDescriptor::dump(dst, spaces, index);
     dst->appendFormat("%*s- Stream: %d flags: %08x\n", spaces, "", mStream, mFlags);
+    dst->appendFormat("%*s- Refcount: %d\n", spaces, "", mActivityCount);
 }
 
 std::string TrackClientDescriptor::toShortString() const
@@ -82,10 +85,10 @@
 SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid,
          audio_attributes_t attributes, const sp<AudioPatch>& patchDesc,
          const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream,
-         product_strategy_t strategy) :
+         product_strategy_t strategy, VolumeSource volumeSource) :
     TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
         AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
-        stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false,
+        stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
         {} /* Sources do not support secondary outputs*/),
         mPatchDesc(patchDesc), mSrcDevice(srcDevice)
 {
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index f486dca..719ef01 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -224,16 +224,16 @@
     audio_devices_t devices = AUDIO_DEVICE_NONE;
     if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
             !is_state_in_call(getPhoneState()) &&
-            !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
-                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
-            outputs.isStreamActive(AUDIO_STREAM_MUSIC,
-                                   SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
+            !outputs.isActiveRemotely(streamToVolumeSource(AUDIO_STREAM_MUSIC),
+                                      SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
+            outputs.isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC),
+                             SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
         product_strategy_t strategyForMedia =
                 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
         devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
     } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
-        (outputs.isStreamActive(AUDIO_STREAM_RING) ||
-         outputs.isStreamActive(AUDIO_STREAM_ALARM))) {
+        (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) ||
+         outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM)))) {
             // do not route accessibility prompts to a digital output currently configured with a
             // compressed format as they would likely not be mixed and dropped.
             // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 93af8a6..43023a8 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -182,16 +182,17 @@
         break;
 
     case STRATEGY_SONIFICATION_RESPECTFUL:
-        if (isInCall() || outputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL)) {
+        if (isInCall() || outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
             device = getDeviceForStrategyInt(
                     STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
                     outputDeviceTypesToIgnore);
         } else {
             bool media_active_locally =
-                    outputs.isStreamActiveLocally(
-                            AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
-                    || outputs.isStreamActiveLocally(
-                            AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+                    outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_MUSIC),
+                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+                    || outputs.isActiveLocally(
+                        streamToVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
+                        SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
             // routing is same as media without the "remote" device
             device = getDeviceForStrategyInt(STRATEGY_MEDIA,
                     availableOutputDevices,
@@ -324,7 +325,8 @@
     case STRATEGY_SONIFICATION:
 
         // If incall, just select the STRATEGY_PHONE device
-        if (isInCall() || outputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL)) {
+        if (isInCall() ||
+                outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
             device = getDeviceForStrategyInt(
                     STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
                     outputDeviceTypesToIgnore);
@@ -397,8 +399,8 @@
             }
             availableOutputDevices =
                     availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType);
-            if (outputs.isStreamActive(AUDIO_STREAM_RING) ||
-                    outputs.isStreamActive(AUDIO_STREAM_ALARM)) {
+            if (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) ||
+                    outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM))) {
                 return getDeviceForStrategyInt(
                     STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
                     outputDeviceTypesToIgnore);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 32cc380..3366141 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1078,6 +1078,7 @@
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                   sanitizedRequestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
+                                  streamToVolumeSource(*stream),
                                   *flags, isRequestedDeviceForExclusiveUse,
                                   std::move(weakSecondaryOutputDescs));
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
@@ -1569,11 +1570,13 @@
 
     *delayMs = 0;
     audio_stream_type_t stream = client->stream();
+    auto clientVolSrc = client->volumeSource();
     auto clientStrategy = client->strategy();
     auto clientAttr = client->attributes();
     if (stream == AUDIO_STREAM_TTS) {
         ALOGV("\t found BEACON stream");
-        if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
+        if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(
+                                    streamToVolumeSource(AUDIO_STREAM_TTS) /*sourceToIgnore*/)) {
             return INVALID_OPERATION;
         } else {
             beaconMuteLatency = handleEventForBeacon(STARTING_BEACON);
@@ -1628,7 +1631,7 @@
         selectOutputForMusicEffects();
     }
 
-    if (outputDesc->streamActiveCount(stream) == 1 || !devices.isEmpty()) {
+    if (outputDesc->getActivityCount(clientVolSrc) == 1 || !devices.isEmpty()) {
         // starting an output being rerouted?
         if (devices.isEmpty()) {
             devices = getNewOutputDevices(outputDesc, false /*fromCache*/);
@@ -1754,11 +1757,12 @@
 {
     // always handle stream stop, check which stream type is stopping
     audio_stream_type_t stream = client->stream();
+    auto clientVolSrc = client->volumeSource();
 
     handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
 
-    if (outputDesc->streamActiveCount(stream) > 0) {
-        if (outputDesc->streamActiveCount(stream) == 1) {
+    if (outputDesc->getActivityCount(clientVolSrc) > 0) {
+        if (outputDesc->getActivityCount(clientVolSrc) == 1) {
             // Automatically disable the remote submix input when output is stopped on a
             // re routing mix of type MIX_TYPE_RECORDERS
             if (audio_is_remote_submix_device(outputDesc->devices().types()) &&
@@ -1780,7 +1784,7 @@
         outputDesc->setClientActive(client, false);
 
         // store time at which the stream was stopped - see isStreamActive()
-        if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) {
+        if (outputDesc->getActivityCount(clientVolSrc) == 0 || forceDeviceUpdate) {
             outputDesc->setStopTime(client, systemTime());
             DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/);
             // delay the device switch by twice the latency because stopOutput() is executed when
@@ -2411,7 +2415,7 @@
             if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) {
                 continue;
             }
-            if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) {
+            if (!(desc->isActive(streamToVolumeSource((audio_stream_type_t)curStream)) || isInCall())) {
                 continue;
             }
             audio_devices_t curStreamDevice = Volume::getDeviceForVolume(
@@ -2499,7 +2503,7 @@
 
         for (audio_io_handle_t output : outputs) {
             sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
-            if (activeOnly && !desc->isStreamActive(AUDIO_STREAM_MUSIC)) {
+            if (activeOnly && !desc->isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC))) {
                 continue;
             }
             ALOGV("selectOutputForMusicEffects activeOnly %d output %d flags 0x%08x",
@@ -2593,14 +2597,14 @@
         if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
             continue;
         }
-        active = mOutputs.isStreamActive((audio_stream_type_t)curStream, inPastMs);
+        active = mOutputs.isActive(streamToVolumeSource((audio_stream_type_t)curStream), inPastMs);
     }
     return active;
 }
 
 bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
 {
-    return mOutputs.isStreamActiveRemotely(stream, inPastMs);
+    return mOutputs.isActiveRemotely(streamToVolumeSource((audio_stream_type_t)stream), inPastMs);
 }
 
 bool AudioPolicyManager::isSourceActive(audio_source_t source) const
@@ -3630,10 +3634,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));
+    sp<SourceClientDescriptor> sourceDesc = new SourceClientDescriptor(
+                *portId, uid, *attributes, patchDesc, srcDevice,
+                mEngine->getStreamTypeForAttributes(*attributes),
+                mEngine->getProductStrategyForAttributes(*attributes),
+                streamToVolumeSource(mEngine->getStreamTypeForAttributes(*attributes)));
 
     status_t status = connectAudioSource(sourceDesc);
     if (status == NO_ERROR) {
@@ -4704,37 +4709,31 @@
 {
     ALOGV("closeOutput(%d)", output);
 
-    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-    if (outputDesc == NULL) {
+    sp<SwAudioOutputDescriptor> closingOutput = mOutputs.valueFor(output);
+    if (closingOutput == NULL) {
         ALOGW("closeOutput() unknown output %d", output);
         return;
     }
-    mPolicyMixes.closeOutput(outputDesc);
+    mPolicyMixes.closeOutput(closingOutput);
 
     // look for duplicated outputs connected to the output being removed.
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        sp<SwAudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
-        if (dupOutputDesc->isDuplicated() &&
-                (dupOutputDesc->mOutput1 == outputDesc ||
-                dupOutputDesc->mOutput2 == outputDesc)) {
-            sp<SwAudioOutputDescriptor> outputDesc2;
-            if (dupOutputDesc->mOutput1 == outputDesc) {
-                outputDesc2 = dupOutputDesc->mOutput2;
-            } else {
-                outputDesc2 = dupOutputDesc->mOutput1;
-            }
+        sp<SwAudioOutputDescriptor> dupOutput = mOutputs.valueAt(i);
+        if (dupOutput->isDuplicated() &&
+                (dupOutput->mOutput1 == closingOutput || dupOutput->mOutput2 == closingOutput)) {
+            sp<SwAudioOutputDescriptor> remainingOutput =
+                dupOutput->mOutput1 == closingOutput ? dupOutput->mOutput2 : dupOutput->mOutput1;
             // As all active tracks on duplicated output will be deleted,
             // and as they were also referenced on the other output, the reference
             // count for their stream type must be adjusted accordingly on
             // the other output.
-            const bool wasActive = outputDesc2->isActive();
-            for (const auto &clientPair : dupOutputDesc->getActiveClients()) {
-                outputDesc2->changeStreamActiveCount(clientPair.first, -clientPair.second);
-            }
+            const bool wasActive = remainingOutput->isActive();
+            // Note: no-op on the closing output where all clients has already been set inactive
+            dupOutput->setAllClientsInactive();
             // stop() will be a no op if the output is still active but is needed in case all
             // active streams refcounts where cleared above
             if (wasActive) {
-                outputDesc2->stop();
+                remainingOutput->stop();
             }
             audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i);
             ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput);
@@ -4746,7 +4745,7 @@
 
     nextAudioPortGeneration();
 
-    ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
+    ssize_t index = mAudioPatches.indexOfKey(closingOutput->getPatchHandle());
     if (index >= 0) {
         sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
         (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
@@ -4754,7 +4753,7 @@
         mpClientInterface->onAudioPatchListUpdate();
     }
 
-    outputDesc->close();
+    closingOutput->close();
 
     removeOutput(output);
     mPreviousOutputs = mOutputs;
@@ -5105,7 +5104,7 @@
         devices.merge(curDevices);
         for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) {
             sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-            if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) {
+            if (outputDesc->isActive(streamToVolumeSource((audio_stream_type_t)curStream))) {
                 activeDevices.merge(outputDesc->devices());
             }
         }
@@ -5519,7 +5518,7 @@
 
     // in-call: always cap volume by voice volume + some low headroom
     if ((stream != AUDIO_STREAM_VOICE_CALL) &&
-            (isInCall() || mOutputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL))) {
+            (isInCall() || mOutputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
         switch (stream) {
         case AUDIO_STREAM_SYSTEM:
         case AUDIO_STREAM_RING:
@@ -5637,9 +5636,8 @@
                                                    bool force)
 {
     // do not change actual stream volume if the stream is muted
-    if (outputDesc->mMuteCount[stream] != 0) {
-        ALOGVV("checkAndSetVolume() stream %d muted count %d",
-              stream, outputDesc->mMuteCount[stream]);
+    if (outputDesc->isMuted(streamToVolumeSource(stream))) {
+        ALOGVV("%s() stream %d muted count %d", __func__, stream, outputDesc->getMuteCount(stream));
         return NO_ERROR;
     }
     audio_policy_forced_cfg_t forceUseForComm =
@@ -5726,10 +5724,10 @@
     }
 
     ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
-          stream, on, outputDesc->mMuteCount[stream], device);
+          stream, on, outputDesc->getMuteCount(stream), device);
 
     if (on) {
-        if (outputDesc->mMuteCount[stream] == 0) {
+        if (!outputDesc->isMuted(streamToVolumeSource(stream))) {
             if (mVolumeCurves->canBeMuted(stream) &&
                     ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
                      (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
@@ -5737,13 +5735,13 @@
             }
         }
         // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
-        outputDesc->mMuteCount[stream]++;
+        outputDesc->incMuteCount(streamToVolumeSource(stream));
     } else {
-        if (outputDesc->mMuteCount[stream] == 0) {
+        if (!outputDesc->isMuted(streamToVolumeSource(stream))) {
             ALOGV("setStreamMute() unmuting non muted stream!");
             return;
         }
-        if (--outputDesc->mMuteCount[stream] == 0) {
+        if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) {
             checkAndSetVolume(stream,
                               mVolumeCurves->getVolumeIndex(stream, device),
                               outputDesc,