Merge "audio policy: fix volume burst on user switch." into nyc-dev
diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h
index 8dc3eda..a3de686 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h
@@ -42,6 +42,8 @@
     {
         switchVolumeCurve(stream, stream);
     }
+    virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
+                                         audio_devices_t device) const = 0;
 
     virtual status_t dump(int fd) const = 0;
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h
index af178f9..424df84 100644
--- a/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h
@@ -38,6 +38,11 @@
     int getVolumeIndexMax() const { return mIndexMax; }
     void setVolumeIndexMin(int volIndexMin);
     void setVolumeIndexMax(int volIndexMax);
+    bool hasVolumeIndexForDevice(audio_devices_t device) const
+    {
+        device = Volume::getDeviceForVolume(device);
+        return mIndexCur.indexOfKey(device) >= 0;
+    }
 
     void dump(int fd) const;
 
@@ -85,6 +90,11 @@
     virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax);
     virtual void initializeVolumeCurves(bool isSpeakerDrcEnabled);
     virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst);
+    virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
+                                         audio_devices_t device) const
+    {
+        return valueFor(stream).hasVolumeIndexForDevice(device);
+    }
 
     virtual status_t dump(int fd) const;
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h
index 009a26f..7c486c8 100644
--- a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h
+++ b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h
@@ -44,8 +44,8 @@
     return lhs.mIndex < rhs.mIndex;
 }
 
-// A volume curve for a given use case and device cateory
-// It contains of list of points of this cuive expressing the atteunation in Millibels for
+// A volume curve for a given use case and device category
+// It contains of list of points of this curve expressing the attenuation in Millibels for
 // a given volume index from 0 to 100
 class VolumeCurve : public RefBase
 {
@@ -105,6 +105,12 @@
     void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; }
     int getVolumeIndexMax() const { return mIndexMax; }
 
+    bool hasVolumeIndexForDevice(audio_devices_t device) const
+    {
+        device = Volume::getDeviceForVolume(device);
+        return mIndexCur.indexOfKey(device) >= 0;
+    }
+
     const sp<VolumeCurve> getOriginVolumeCurve(device_category deviceCategory) const
     {
         ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category");
@@ -200,6 +206,11 @@
     {
         return getCurvesFor(stream).volIndexToDb(cat, indexInUi);
     }
+    virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
+                                         audio_devices_t device) const
+    {
+        return getCurvesFor(stream).hasVolumeIndexForDevice(device);
+    }
 
     virtual status_t dump(int fd) const;
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 35c868e..07c470d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1821,15 +1821,9 @@
     // Force max volume if stream cannot be muted
     if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);
 
-    ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d",
+    ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d",
           stream, device, index);
 
-    // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and
-    // clear all device specific values
-    if (device == AUDIO_DEVICE_OUT_DEFAULT) {
-        mVolumeCurves->clearCurrentVolumeIndex(stream);
-    }
-
     // 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)) {
@@ -1838,8 +1832,14 @@
         mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index);
     }
 
-    // update volume on all outputs whose current device is also selected by the same
-    // strategy as the device specified by the caller
+    // 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 strategy corresponding to 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 strategy
+    // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT), 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);
@@ -1848,16 +1848,23 @@
             if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
                 continue;
             }
+            if (!desc->isStreamActive((audio_stream_type_t)curStream)) {
+                continue;
+            }
             routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
             audio_devices_t curStreamDevice = getDeviceForStrategy(curStrategy, true /*fromCache*/);
-            // it is possible that the requested device is not selected by the strategy
-            // (e.g an explicit audio patch is active causing getDevicesForStream()
-            // to return this device. We must make sure that the device passed is part of the
-            // devices considered when applying volume below.
-            curStreamDevice |= device;
+            if ((curStreamDevice & device) == 0) {
+                continue;
+            }
+            bool applyDefault = false;
+            if (device != AUDIO_DEVICE_OUT_DEFAULT) {
+                curStreamDevice |= device;
+            } else if (!mVolumeCurves->hasVolumeIndexForDevice(
+                    stream, Volume::getDeviceForVolume(curStreamDevice))) {
+                applyDefault = true;
+            }
 
-            if (((device == AUDIO_DEVICE_OUT_DEFAULT) ||
-                    ((curDevice & curStreamDevice) != 0))) {
+            if (applyDefault || ((curDevice & curStreamDevice) != 0)) {
                 status_t volStatus =
                         checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice);
                 if (volStatus != NO_ERROR) {