1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include "IVolumeCurvesCollection.h"
20 #include <policy.h>
21 #include <utils/RefBase.h>
22 #include <utils/String8.h>
23 #include <utils/SortedVector.h>
24 #include <utils/KeyedVector.h>
25 #include <system/audio.h>
26 #include <cutils/config_utils.h>
27 #include <string>
28 #include <utility>
29 
30 namespace android {
31 
32 struct CurvePoint
33 {
CurvePointCurvePoint34     CurvePoint() {}
CurvePointCurvePoint35     CurvePoint(int index, int attenuationInMb) :
36         mIndex(index), mAttenuationInMb(attenuationInMb) {}
37     uint32_t mIndex;
38     int mAttenuationInMb;
39 };
40 
41 inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs)
42 {
43     return lhs.mIndex < rhs.mIndex;
44 }
45 
46 // A volume curve for a given use case and device category
47 // It contains of list of points of this curve expressing the attenuation in Millibels for
48 // a given volume index from 0 to 100
49 class VolumeCurve : public RefBase
50 {
51 public:
VolumeCurve(device_category device,audio_stream_type_t stream)52     VolumeCurve(device_category device, audio_stream_type_t stream) :
53         mDeviceCategory(device), mStreamType(stream) {}
54 
getDeviceCategory()55     device_category getDeviceCategory() const { return mDeviceCategory; }
getStreamType()56     audio_stream_type_t getStreamType() const { return mStreamType; }
57 
add(const CurvePoint & point)58     void add(const CurvePoint &point) { mCurvePoints.add(point); }
59 
60     float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const;
61 
62     void dump(int fd) const;
63 
64 private:
65     SortedVector<CurvePoint> mCurvePoints;
66     device_category mDeviceCategory;
67     audio_stream_type_t mStreamType;
68 };
69 
70 // Volume Curves for a given use case indexed by device category
71 class VolumeCurvesForStream : public KeyedVector<device_category, sp<VolumeCurve> >
72 {
73 public:
VolumeCurvesForStream()74     VolumeCurvesForStream() : mIndexMin(0), mIndexMax(1), mCanBeMuted(true)
75     {
76         mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0);
77     }
78 
getCurvesFor(device_category device)79     sp<VolumeCurve> getCurvesFor(device_category device) const
80     {
81         if (indexOfKey(device) < 0) {
82             return 0;
83         }
84         return valueFor(device);
85     }
86 
getVolumeIndex(audio_devices_t device)87     int getVolumeIndex(audio_devices_t device) const
88     {
89         device = Volume::getDeviceForVolume(device);
90         // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME
91         if (mIndexCur.indexOfKey(device) < 0) {
92             device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME;
93         }
94         return mIndexCur.valueFor(device);
95     }
96 
canBeMuted()97     bool canBeMuted() const { return mCanBeMuted; }
clearCurrentVolumeIndex()98     void clearCurrentVolumeIndex() { mIndexCur.clear(); }
addCurrentVolumeIndex(audio_devices_t device,int index)99     void addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); }
100 
setVolumeIndexMin(int volIndexMin)101     void setVolumeIndexMin(int volIndexMin) { mIndexMin = volIndexMin; }
getVolumeIndexMin()102     int getVolumeIndexMin() const { return mIndexMin; }
103 
setVolumeIndexMax(int volIndexMax)104     void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; }
getVolumeIndexMax()105     int getVolumeIndexMax() const { return mIndexMax; }
106 
hasVolumeIndexForDevice(audio_devices_t device)107     bool hasVolumeIndexForDevice(audio_devices_t device) const
108     {
109         device = Volume::getDeviceForVolume(device);
110         return mIndexCur.indexOfKey(device) >= 0;
111     }
112 
getOriginVolumeCurve(device_category deviceCategory)113     const sp<VolumeCurve> getOriginVolumeCurve(device_category deviceCategory) const
114     {
115         ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category");
116         return mOriginVolumeCurves.valueFor(deviceCategory);
117     }
setVolumeCurve(device_category deviceCategory,const sp<VolumeCurve> & volumeCurve)118     void setVolumeCurve(device_category deviceCategory, const sp<VolumeCurve> &volumeCurve)
119     {
120         ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve");
121         replaceValueFor(deviceCategory, volumeCurve);
122     }
123 
add(const sp<VolumeCurve> & volumeCurve)124     ssize_t add(const sp<VolumeCurve> &volumeCurve)
125     {
126         device_category deviceCategory = volumeCurve->getDeviceCategory();
127         ssize_t index = indexOfKey(deviceCategory);
128         if (index < 0) {
129             // Keep track of original Volume Curves per device category in order to switch curves.
130             mOriginVolumeCurves.add(deviceCategory, volumeCurve);
131             return KeyedVector::add(deviceCategory, volumeCurve);
132         }
133         return index;
134     }
135 
volIndexToDb(device_category deviceCat,int indexInUi)136     float volIndexToDb(device_category deviceCat, int indexInUi) const
137     {
138         return getCurvesFor(deviceCat)->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
139     }
140 
141     void dump(int fd, int spaces, bool curvePoints = false) const;
142 
143 private:
144     KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves;
145     KeyedVector<audio_devices_t, int> mIndexCur; /**< current volume index per device. */
146     int mIndexMin; /**< min volume index. */
147     int mIndexMax; /**< max volume index. */
148     bool mCanBeMuted; /**< true is the stream can be muted. */
149 };
150 
151 // Collection of Volume Curves indexed by use case
152 class VolumeCurvesCollection : public KeyedVector<audio_stream_type_t, VolumeCurvesForStream>,
153                                public IVolumeCurvesCollection
154 {
155 public:
VolumeCurvesCollection()156     VolumeCurvesCollection()
157     {
158         // Create an empty collection of curves
159         for (ssize_t i = 0 ; i < AUDIO_STREAM_CNT; i++) {
160             audio_stream_type_t stream = static_cast<audio_stream_type_t>(i);
161             KeyedVector::add(stream, VolumeCurvesForStream());
162         }
163     }
164 
165     // Once XML has been parsed, must be call first to sanity check table and initialize indexes
initStreamVolume(audio_stream_type_t stream,int indexMin,int indexMax)166     virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
167     {
168         editValueAt(stream).setVolumeIndexMin(indexMin);
169         editValueAt(stream).setVolumeIndexMax(indexMax);
170         return NO_ERROR;
171     }
clearCurrentVolumeIndex(audio_stream_type_t stream)172     virtual void clearCurrentVolumeIndex(audio_stream_type_t stream)
173     {
174         editCurvesFor(stream).clearCurrentVolumeIndex();
175     }
addCurrentVolumeIndex(audio_stream_type_t stream,audio_devices_t device,int index)176     virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index)
177     {
178         editCurvesFor(stream).addCurrentVolumeIndex(device, index);
179     }
canBeMuted(audio_stream_type_t stream)180     virtual bool canBeMuted(audio_stream_type_t stream) { return getCurvesFor(stream).canBeMuted(); }
181 
getVolumeIndexMin(audio_stream_type_t stream)182     virtual int getVolumeIndexMin(audio_stream_type_t stream) const
183     {
184         return getCurvesFor(stream).getVolumeIndexMin();
185     }
getVolumeIndexMax(audio_stream_type_t stream)186     virtual int getVolumeIndexMax(audio_stream_type_t stream) const
187     {
188         return getCurvesFor(stream).getVolumeIndexMax();
189     }
getVolumeIndex(audio_stream_type_t stream,audio_devices_t device)190     virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device)
191     {
192         return getCurvesFor(stream).getVolumeIndex(device);
193     }
switchVolumeCurve(audio_stream_type_t streamSrc,audio_stream_type_t streamDst)194     virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
195     {
196         const VolumeCurvesForStream &sourceCurves = getCurvesFor(streamSrc);
197         VolumeCurvesForStream &dstCurves = editCurvesFor(streamDst);
198         ALOG_ASSERT(sourceCurves.size() == dstCurves.size(), "device category not aligned");
199         for (size_t index = 0; index < sourceCurves.size(); index++) {
200             device_category cat = sourceCurves.keyAt(index);
201             dstCurves.setVolumeCurve(cat, sourceCurves.getOriginVolumeCurve(cat));
202         }
203     }
volIndexToDb(audio_stream_type_t stream,device_category cat,int indexInUi)204     virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const
205     {
206         return getCurvesFor(stream).volIndexToDb(cat, indexInUi);
207     }
hasVolumeIndexForDevice(audio_stream_type_t stream,audio_devices_t device)208     virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream,
209                                          audio_devices_t device) const
210     {
211         return getCurvesFor(stream).hasVolumeIndexForDevice(device);
212     }
213 
214     virtual status_t dump(int fd) const;
215 
add(const sp<VolumeCurve> & volumeCurve)216     ssize_t add(const sp<VolumeCurve> &volumeCurve)
217     {
218         audio_stream_type_t streamType = volumeCurve->getStreamType();
219         return editCurvesFor(streamType).add(volumeCurve);
220     }
editCurvesFor(audio_stream_type_t stream)221     VolumeCurvesForStream &editCurvesFor(audio_stream_type_t stream)
222     {
223         ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve");
224         return editValueAt(stream);
225     }
getCurvesFor(audio_stream_type_t stream)226     const VolumeCurvesForStream &getCurvesFor(audio_stream_type_t stream) const
227     {
228         ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve");
229         return valueFor(stream);
230     }
231 };
232 
233 }; // namespace android
234