Revert "Revert "audiopolicy: moves Stream Volume Curves to Engine""

This reverts commit efdbe3bfeff7c150d99900fddca005d48048f188.

Test: make
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
index 5c33fb3..bc027e2 100644
--- a/services/audiopolicy/engine/common/include/EngineBase.h
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -19,6 +19,7 @@
 #include <EngineConfig.h>
 #include <AudioPolicyManagerInterface.h>
 #include <ProductStrategy.h>
+#include <StreamVolumeCurves.h>
 
 namespace android {
 namespace audio_policy {
@@ -67,6 +68,10 @@
 
     status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override;
 
+    VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override;
+
+    VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override;
+
     void dump(String8 *dst) const override;
 
 
@@ -87,7 +92,16 @@
         return is_state_in_call(getPhoneState());
     }
 
-private:
+    VolumeSource toVolumeSource(audio_stream_type_t stream) const
+    {
+        return static_cast<VolumeSource>(stream);
+    }
+
+    status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst);
+
+    status_t restoreOriginVolumeCurve(audio_stream_type_t stream);
+
+ private:
     AudioPolicyManagerObserver *mApmObserver = nullptr;
 
     ProductStrategyMap mProductStrategies;
@@ -95,6 +109,8 @@
 
     /** current forced use configuration. */
     audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {};
+
+    StreamVolumeCurves mStreamVolumeCurves;
 };
 
 } // namespace audio_policy
diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h
new file mode 100644
index 0000000..5b0b7d6
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <VolumeCurve.h>
+#include <map>
+
+namespace android {
+
+class StreamVolumeCurves
+{
+public:
+    StreamVolumeCurves() = default;
+
+    /**
+     * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves
+     * from one stream type to another.
+     * @param src source stream type
+     * @param dst destination stream type
+     */
+    status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
+    {
+        if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) {
+            ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst);
+            return NO_INIT;
+        }
+        const VolumeCurves &sourceCurves = getCurvesFor(streamSrc);
+        VolumeCurves &dstCurves = editCurvesFor(streamDst);
+        return dstCurves.switchCurvesFrom(sourceCurves);
+    }
+    void dump(String8 *dst, int spaces = 0) const;
+
+    void add(const VolumeCurves &curves, audio_stream_type_t streamType)
+    {
+        mCurves.emplace(streamType, curves);
+    }
+
+    bool hasCurvesFor(audio_stream_type_t stream)
+    {
+        return mCurves.find(stream) != end(mCurves);
+    }
+
+    VolumeCurves &editCurvesFor(audio_stream_type_t stream)
+    {
+        ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+        return mCurves[stream];
+    }
+    const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const
+    {
+        ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+        return mCurves.at(stream);
+    }
+    /**
+     * @brief getVolumeCurvesForStream
+     * @param stream type for which the volume curves interface is requested
+     * @return the VolumeCurves for a given stream type.
+     */
+    VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream)
+    {
+        ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve");
+        return mCurves[stream];
+    }
+    /**
+     * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume
+     * curves for a given stream type
+     * @param stream for which the volume curves will be restored.
+     */
+    status_t restoreOriginVolumeCurve(audio_stream_type_t stream)
+    {
+        if (!hasCurvesFor(stream)) {
+            ALOGE("%s: No curves defined for streams", __FUNCTION__);
+            return NO_INIT;
+        }
+        return switchVolumeCurve(stream, stream);
+    }
+
+private:
+    std::map<audio_stream_type_t, VolumeCurves> mCurves;
+};
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h
new file mode 100644
index 0000000..c2a1da7
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/VolumeCurve.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "IVolumeCurves.h"
+#include <policy.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <system/audio.h>
+#include <cutils/config_utils.h>
+#include <string>
+#include <map>
+#include <utility>
+
+namespace android {
+
+struct CurvePoint
+{
+    CurvePoint() {}
+    CurvePoint(int index, int attenuationInMb) :
+        mIndex(index), mAttenuationInMb(attenuationInMb) {}
+    uint32_t mIndex;
+    int mAttenuationInMb;
+};
+
+inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs)
+{
+    return lhs.mIndex < rhs.mIndex;
+}
+
+// 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
+{
+public:
+    VolumeCurve(device_category device) : mDeviceCategory(device) {}
+
+    void add(const CurvePoint &point) { mCurvePoints.add(point); }
+
+    float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const;
+
+    void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const;
+
+    device_category getDeviceCategory() const { return mDeviceCategory; }
+
+private:
+    const device_category mDeviceCategory;
+    SortedVector<CurvePoint> mCurvePoints;
+};
+
+// Volume Curves for a given use case indexed by device category
+class VolumeCurves : public KeyedVector<device_category, sp<VolumeCurve> >,
+                     public IVolumeCurves
+{
+public:
+    VolumeCurves(int indexMin = 0, int indexMax = 100) :
+        mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT)
+    {
+        addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
+    }
+    VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) :
+        mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream)
+    {
+        addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
+    }
+
+    // Once XML has been parsed, must be call first to sanity check table and initialize indexes
+    virtual status_t initVolume(int indexMin, int indexMax)
+    {
+        mIndexMin = indexMin;
+        mIndexMax = indexMax;
+        return NO_ERROR;
+    }
+
+    sp<VolumeCurve> getCurvesFor(device_category device) const
+    {
+        if (indexOfKey(device) < 0) {
+            return 0;
+        }
+        return valueFor(device);
+    }
+
+    virtual int getVolumeIndex(audio_devices_t device) const
+    {
+        device = Volume::getDeviceForVolume(device);
+        // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME
+        if (mIndexCur.find(device) == end(mIndexCur)) {
+            device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME;
+        }
+        return mIndexCur.at(device);
+    }
+
+    virtual bool canBeMuted() const { return mCanBeMuted; }
+    virtual void clearCurrentVolumeIndex() { mIndexCur.clear(); }
+    void addCurrentVolumeIndex(audio_devices_t device, int index) override
+    {
+        mIndexCur[device] = index;
+    }
+
+    int getVolumeIndexMin() const { return mIndexMin; }
+
+    int getVolumeIndexMax() const { return mIndexMax; }
+
+    bool hasVolumeIndexForDevice(audio_devices_t device) const
+    {
+        device = Volume::getDeviceForVolume(device);
+        return mIndexCur.find(device) != end(mIndexCur);
+    }
+
+    status_t switchCurvesFrom(const VolumeCurves &referenceCurves)
+    {
+        if (size() != referenceCurves.size()) {
+            ALOGE("%s! device category not aligned, cannot switch", __FUNCTION__);
+            return BAD_TYPE;
+        }
+        for (size_t index = 0; index < size(); index++) {
+            device_category cat = keyAt(index);
+            setVolumeCurve(cat, referenceCurves.getOriginVolumeCurve(cat));
+        }
+        return NO_ERROR;
+    }
+    status_t restoreOriginVolumeCurve()
+    {
+        return switchCurvesFrom(*this);
+    }
+
+    const sp<VolumeCurve> getOriginVolumeCurve(device_category deviceCategory) const
+    {
+        ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category");
+        return mOriginVolumeCurves.valueFor(deviceCategory);
+    }
+    void setVolumeCurve(device_category deviceCategory, const sp<VolumeCurve> &volumeCurve)
+    {
+        ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve");
+        replaceValueFor(deviceCategory, volumeCurve);
+    }
+
+    ssize_t add(const sp<VolumeCurve> &volumeCurve)
+    {
+        device_category deviceCategory = volumeCurve->getDeviceCategory();
+        ssize_t index = indexOfKey(deviceCategory);
+        if (index < 0) {
+            // Keep track of original Volume Curves per device category in order to switch curves.
+            mOriginVolumeCurves.add(deviceCategory, volumeCurve);
+            return KeyedVector::add(deviceCategory, volumeCurve);
+        }
+        return index;
+    }
+
+    virtual float volIndexToDb(device_category deviceCat, int indexInUi) const
+    {
+        sp<VolumeCurve> vc = getCurvesFor(deviceCat);
+        if (vc != 0) {
+            return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
+        } else {
+            ALOGE("Invalid device category %d for Volume Curve", deviceCat);
+            return 0.0f;
+        }
+    }
+
+    audio_stream_type_t getStreamType() const { return mStream; }
+
+    void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override;
+
+private:
+    KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves;
+    std::map<audio_devices_t, int> mIndexCur; /**< current volume index per device. */
+    /*const*/ int mIndexMin; /**< min volume index. */
+    /*const*/ int mIndexMax; /**< max volume index. */
+    const bool mCanBeMuted = true; /**< true is the stream can be muted. */
+
+    const audio_stream_type_t mStream; /**< Keep it for legacy. */
+};
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
index 755f2a8..2449b61 100644
--- a/services/audiopolicy/engine/common/src/EngineBase.cpp
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -55,8 +55,10 @@
 
     if (!is_state_in_call(oldState) && is_state_in_call(state)) {
         ALOGV("  Entering call in setPhoneState()");
+        switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF);
     } else if (is_state_in_call(oldState) && !is_state_in_call(state)) {
         ALOGV("  Exiting call in setPhoneState()");
+        restoreOriginVolumeCurve(AUDIO_STREAM_DTMF);
     }
     return NO_ERROR;
 }
@@ -108,6 +110,31 @@
             productStrategies[strategyId] = strategy;
         }
     };
+    auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) {
+        for (auto &configVolume : configVolumes) {
+            audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
+            if (configVolume.stream.empty() ||
+                    !StreamTypeConverter::fromString(configVolume.stream, streamType)) {
+                ALOGE("%s: Invalid stream type", __FUNCTION__);
+                continue;
+            }
+            VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax);
+            for (auto &configCurve : configVolume.volumeCurves) {
+                device_category deviceCategory = DEVICE_CATEGORY_SPEAKER;
+                if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory,
+                                                         deviceCategory)) {
+                    ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
+                    continue;
+                }
+                sp<VolumeCurve> curve = new VolumeCurve(deviceCategory);
+                for (auto &point : configCurve.curvePoints) {
+                    curve->add({point.index, point.attenuationInMb});
+                }
+                volumeCurves.add(curve);
+            }
+            streamVolumeCollection.add(volumeCurves, streamType);
+        }
+    };
 
     auto result = engineConfig::parse();
     if (result.parsedConfig == nullptr) {
@@ -116,6 +143,7 @@
     }
     ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
     loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies);
+    loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves);
     return result;
 }
 
@@ -173,9 +201,30 @@
     return NO_ERROR;
 }
 
+VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr)
+{
+    return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr));
+}
+
+VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream)
+{
+    return &mStreamVolumeCurves.getVolumeCurvesForStream(stream);
+}
+
+status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
+{
+    return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);;
+}
+
+status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream)
+{
+    return mStreamVolumeCurves.restoreOriginVolumeCurve(stream);
+}
+
 void EngineBase::dump(String8 *dst) const
 {
     mProductStrategies.dump(dst, 2);
+    mStreamVolumeCurves.dump(dst, 2);
 }
 
 } // namespace audio_policy
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index 3940c0c..86a59b0 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -131,10 +131,131 @@
     }
 };
 
+const engineConfig::VolumeGroups gVolumeGroups = {
+    {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } },
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } },
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } },
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } },
+     },
+    },
+    {"system", "AUDIO_STREAM_SYSTEM", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } },
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } },
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"ring", "AUDIO_STREAM_RING", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } },
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"music", "AUDIO_STREAM_MUSIC", 0, 40,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"alarm", "AUDIO_STREAM_ALARM", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE
+     },
+    },
+    {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } },
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } },
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } },
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"dtmf", "AUDIO_STREAM_DTMF", 0, 100,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } },
+         {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"tts", "AUDIO_STREAM_TTS", 0, 16,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE
+     },
+    },
+    {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE
+     },
+    },
+    {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+     },
+    },
+    {"patch", "AUDIO_STREAM_PATCH", 0, 1,
+     {
+         {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+         {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE
+     },
+    },
+};
+
 const engineConfig::Config gDefaultEngineConfig = {
     1.0,
     gOrderedStrategies,
     {},
-    {}
+    {},
+    gVolumeGroups
 };
 } // namespace android
diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp
new file mode 100644
index 0000000..fe3b000
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "APM::Engine::StreamVolumeCurves"
+//#define LOG_NDEBUG 0
+
+#include "StreamVolumeCurves.h"
+#include <TypeConverter.h>
+
+namespace android {
+
+void StreamVolumeCurves::dump(String8 *dst, int spaces) const
+{
+    if (mCurves.empty()) {
+        return;
+    }
+    dst->appendFormat("\n%*sStreams dump:\n", spaces, "");
+    dst->appendFormat(
+                "%*sStream  Can be muted  Index Min  Index Max  Index Cur [device : index]...\n", spaces + 2, "");
+    for (const auto &streamCurve : mCurves) {
+        streamCurve.second.dump(dst, spaces + 2, false);
+    }
+    dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, "");
+    for (const auto &streamCurve : mCurves) {
+        std::string streamTypeLiteral;
+        StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral);
+        dst->appendFormat(
+                    " %s (%02d): Curve points for device category (index, attenuation in millibel)\n",
+                    streamTypeLiteral.c_str(), streamCurve.first);
+        streamCurve.second.dump(dst, spaces + 2, true);
+    }
+}
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
new file mode 100644
index 0000000..be2ca73
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "APM::VolumeCurve"
+//#define LOG_NDEBUG 0
+
+#include "VolumeCurve.h"
+#include "TypeConverter.h"
+
+namespace android {
+
+float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
+{
+    ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
+
+    if (indexInUi < volIndexMin) {
+        // an index of 0 means mute request when volIndexMin > 0
+        if (indexInUi == 0) {
+            ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin);
+            return VOLUME_MIN_DB;
+        }
+        ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
+        indexInUi = volIndexMin;
+    } else if (indexInUi > volIndexMax) {
+        ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
+        indexInUi = volIndexMax;
+    }
+
+    size_t nbCurvePoints = mCurvePoints.size();
+    // the volume index in the UI is relative to the min and max volume indices for this stream
+    int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
+    int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
+
+    // Where would this volume index been inserted in the curve point
+    size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
+    if (indexInUiPosition >= nbCurvePoints) {
+        //use last point of table
+        return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
+    }
+    if (indexInUiPosition == 0) {
+        if (indexInUiPosition != mCurvePoints[0].mIndex) {
+            return VOLUME_MIN_DB; // out of bounds
+        }
+        return mCurvePoints[0].mAttenuationInMb / 100.0f;
+    }
+    // linear interpolation in the attenuation table in dB
+    float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
+            ((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
+                ( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
+                        (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
+                    ((float)(mCurvePoints[indexInUiPosition].mIndex -
+                            mCurvePoints[indexInUiPosition - 1].mIndex)) );
+
+    ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
+            mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
+            mCurvePoints[indexInUiPosition].mIndex,
+            ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
+            ((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));
+
+    return decibels;
+}
+
+void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const
+{
+    if (!curvePoints) {
+        return;
+    }
+    dst->append(" {");
+    for (size_t i = 0; i < mCurvePoints.size(); i++) {
+        dst->appendFormat("%*s (%3d, %5d)", spaces, "",
+                 mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb);
+        dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", ");
+    }
+}
+
+void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const
+{
+    if (!curvePoints) {
+        dst->appendFormat("%*s%02d      %s         %03d        %03d        ", spaces, "",
+                          mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax);
+        for (const auto &pair : mIndexCur) {
+            dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second);
+        }
+        dst->appendFormat("\n");
+        return;
+    }
+    for (size_t i = 0; i < size(); i++) {
+        std::string deviceCatLiteral;
+        DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral);
+        dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str());
+        valueAt(i)->dump(dst, 2, true);
+    }
+}
+
+} // namespace android
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index e18f687..f7caad2 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -45,6 +45,27 @@
 
 using AttributesGroups = std::vector<AttributesGroup>;
 
+struct CurvePoint {
+    int index;
+    int attenuationInMb;
+};
+using CurvePoints = std::vector<CurvePoint>;
+
+struct VolumeCurve {
+    std::string deviceCategory;
+    CurvePoints curvePoints;
+};
+using VolumeCurves = std::vector<VolumeCurve>;
+
+struct VolumeGroup {
+    std::string name;
+    std::string stream;
+    int indexMin;
+    int indexMax;
+    VolumeCurves volumeCurves;
+};
+using VolumeGroups = std::vector<VolumeGroup>;
+
 struct ProductStrategy {
     std::string name;
     AttributesGroups attributesGroups;
@@ -78,6 +99,7 @@
     ProductStrategies productStrategies;
     Criteria criteria;
     CriterionTypes criterionTypes;
+    VolumeGroups volumeGroups;
 };
 
 /** Result of `parse(const char*)` */
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 3aa38cf..227ebd8 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -107,6 +107,35 @@
     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
                                          Collection &collection);
 };
+struct VolumeTraits : public BaseSerializerTraits<VolumeCurve, VolumeCurves> {
+    static constexpr const char *tag = "volume";
+    static constexpr const char *collectionTag = "volumes";
+    static constexpr const char *volumePointTag = "point";
+
+    struct Attributes {
+        static constexpr const char *deviceCategory = "deviceCategory";
+        static constexpr const char *reference = "ref"; /**< For volume curves factorization. */
+    };
+
+    static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
+                                         Collection &collection);
+};
+struct VolumeGroupTraits : public BaseSerializerTraits<VolumeGroup, VolumeGroups> {
+    static constexpr const char *tag = "volumeGroup";
+    static constexpr const char *collectionTag = "volumeGroups";
+
+    struct Attributes {
+        static constexpr const char *name = "name";
+        static constexpr const char *stream = "stream"; // For legacy volume curves
+        static constexpr const char *indexMin = "indexMin";
+        static constexpr const char *indexMax = "indexMax";
+    };
+
+    static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
+                                         Collection &collection);
+};
+
+using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
 
 using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
 
@@ -383,6 +412,100 @@
     return NO_ERROR;
 }
 
+status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
+{
+    std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory);
+    if (deviceCategory.empty()) {
+        ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory);
+    }
+
+    std::string referenceName = getXmlAttribute(root, Attributes::reference);
+    const _xmlNode *ref = NULL;
+    if (!referenceName.empty()) {
+        getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag);
+        if (ref == NULL) {
+            ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str());
+            return BAD_VALUE;
+        }
+    }
+    // Retrieve curve point from reference element if found or directly from current curve
+    CurvePoints curvePoints;
+    for (const xmlNode *child = referenceName.empty() ?
+         root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {
+        if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) {
+            xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+            if (pointXml == NULL) {
+                return BAD_VALUE;
+            }
+            ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast<const char*>(pointXml.get()));
+            std::vector<int> point;
+            collectionFromString<DefaultTraits<int>>(
+                        reinterpret_cast<const char*>(pointXml.get()), point, ",");
+            if (point.size() != 2) {
+                ALOGE("%s: Invalid %s: %s", __func__, volumePointTag,
+                      reinterpret_cast<const char*>(pointXml.get()));
+                return BAD_VALUE;
+            }
+            curvePoints.push_back({point[0], point[1]});
+        }
+    }
+    volumes.push_back({ deviceCategory, curvePoints });
+    return NO_ERROR;
+}
+
+status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes)
+{
+    std::string name;
+    std::string stream = {};
+    int indexMin = 0;
+    int indexMax = 0;
+
+    for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) {
+        if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) {
+            xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+            if (nameXml == nullptr) {
+                return BAD_VALUE;
+            }
+            name = reinterpret_cast<const char*>(nameXml.get());
+        }
+        if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) {
+            xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+            if (streamXml == nullptr) {
+                return BAD_VALUE;
+            }
+            stream = reinterpret_cast<const char*>(streamXml.get());
+        }
+        if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) {
+            xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+            if (indexMinXml == nullptr) {
+                return BAD_VALUE;
+            }
+            std::string indexMinLiteral(reinterpret_cast<const char*>(indexMinXml.get()));
+            if (!convertTo(indexMinLiteral, indexMin)) {
+                return BAD_VALUE;
+            }
+        }
+        if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) {
+            xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);
+            if (indexMaxXml == nullptr) {
+                return BAD_VALUE;
+            }
+            std::string indexMaxLiteral(reinterpret_cast<const char*>(indexMaxXml.get()));
+            if (!convertTo(indexMaxLiteral, indexMax)) {
+                return BAD_VALUE;
+            }
+        }
+    }
+    ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d",
+          __func__, name.c_str(), stream.c_str(), indexMin, indexMax);
+
+    VolumeCurves groupVolumeCurves;
+    size_t skipped = 0;
+    deserializeCollection<VolumeTraits>(doc, root, groupVolumeCurves, skipped);
+    volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves });
+    return NO_ERROR;
+}
+
 ParsingResult parse(const char* path) {
     xmlDocPtr doc;
     doc = xmlParseFile(path);
@@ -414,6 +537,9 @@
                 doc, cur, config->criteria, nbSkippedElements);
     deserializeCollection<CriterionTypeTraits>(
                 doc, cur, config->criterionTypes, nbSkippedElements);
+    deserializeCollection<VolumeGroupTraits>(
+                doc, cur, config->volumeGroups, nbSkippedElements);
+
     return {std::move(config), nbSkippedElements};
 }
 
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index 498cc3b..c9e9507 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -18,6 +18,7 @@
 
 #include <AudioPolicyManagerObserver.h>
 #include <media/AudioProductStrategy.h>
+#include <IVolumeCurves.h>
 #include <policy.h>
 #include <Volume.h>
 #include <HwModule.h>
@@ -234,6 +235,21 @@
      */
     virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0;
 
+    /**
+     * @brief getVolumeCurvesForAttributes retrieves the Volume Curves interface for the
+     *        requested Audio Attributes.
+     * @param attr to be considered
+     * @return IVolumeCurves interface pointer if found, nullptr otherwise
+     */
+    virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0;
+
+    /**
+     * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream
+     * @param stream to be considered
+     * @return IVolumeCurves interface pointer if found, nullptr otherwise
+     */
+    virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0;
+
     virtual void dump(String8 *dst) const = 0;
 
 protected:
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
index b7902cf..43ba625 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <IVolumeCurvesCollection.h>
 #include <AudioGain.h>
 #include <AudioPort.h>
 #include <AudioPatch.h>
@@ -51,8 +50,6 @@
 
     virtual const DeviceVector &getAvailableInputDevices() const = 0;
 
-    virtual IVolumeCurvesCollection &getVolumeCurves() = 0;
-
     virtual const sp<DeviceDescriptor> &getDefaultOutputDevice() const = 0;
 
 protected: