1 /*
2  * Copyright 2017 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 #ifndef ANDROID_VOLUME_SHAPER_H
18 #define ANDROID_VOLUME_SHAPER_H
19 
20 #include <cmath>
21 #include <list>
22 #include <math.h>
23 #include <sstream>
24 
25 #include <android/media/VolumeShaperConfiguration.h>
26 #include <android/media/VolumeShaperConfigurationOptionFlag.h>
27 #include <android/media/VolumeShaperOperation.h>
28 #include <android/media/VolumeShaperOperationFlag.h>
29 #include <android/media/VolumeShaperState.h>
30 #include <binder/Parcel.h>
31 #include <media/Interpolator.h>
32 #include <utils/Mutex.h>
33 #include <utils/RefBase.h>
34 
35 #pragma push_macro("LOG_TAG")
36 #undef LOG_TAG
37 #define LOG_TAG "VolumeShaper"
38 
39 // turn on VolumeShaper logging
40 #define VS_LOGGING 0
41 #define VS_LOG(...) ALOGD_IF(VS_LOGGING, __VA_ARGS__)
42 
43 namespace android {
44 
45 namespace media {
46 
47 // The native VolumeShaper class mirrors the java VolumeShaper class;
48 // in addition, the native class contains implementation for actual operation.
49 //
50 // VolumeShaper methods are not safe for multiple thread access.
51 // Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
52 //
53 // Classes below written are to avoid naked pointers so there are no
54 // explicit destructors required.
55 
56 class VolumeShaper {
57 public:
58     // S and T are like template typenames (matching the Interpolator<S, T>)
59     using S = float; // time type
60     using T = float; // volume type
61 
62 // Curve and dimension information
63 // TODO: member static const or constexpr float initialization not permitted in C++11
64 #define MIN_CURVE_TIME    0.f  // type S: start of VolumeShaper curve (normalized)
65 #define MAX_CURVE_TIME    1.f  // type S: end of VolumeShaper curve (normalized)
66 #define MIN_LINEAR_VOLUME 0.f  // type T: silence / mute audio
67 #define MAX_LINEAR_VOLUME 1.f  // type T: max volume, unity gain
68 #define MAX_LOG_VOLUME    0.f  // type T: max volume, unity gain in dBFS
69 
70     /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
71      * Each system VolumeShapers has a predefined Id, which ranges from 0
72      * to kSystemVolumeShapersMax - 1 and is unique for its usage.
73      *
74      * "1" is reserved for system ducking.
75      */
76     static const int kSystemVolumeShapersMax = 16;
77 
78     /* kUserVolumeShapersMax is the maximum number of application
79      * VolumeShapers for a player/track.  Application VolumeShapers are
80      * assigned on creation by the client, and have Ids ranging
81      * from kSystemVolumeShapersMax to INT32_MAX.
82      *
83      * The number of user/application volume shapers is independent to the
84      * system volume shapers. If an application tries to create more than
85      * kUserVolumeShapersMax to a player, then the apply() will fail.
86      * This prevents exhausting server side resources by a potentially malicious
87      * application.
88      */
89     static const int kUserVolumeShapersMax = 16;
90 
91     /* VolumeShaper::Status is equivalent to status_t if negative
92      * but if non-negative represents the id operated on.
93      * It must be expressible as an int32_t for binder purposes.
94      */
95     using Status = status_t;
96 
97     // Local definition for clamp as std::clamp is included in C++17 only.
98     // TODO: use the std::clamp version when Android build uses C++17.
99     template<typename R>
clamp(const R & v,const R & lo,const R & hi)100     static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
101         return (v < lo) ? lo : (hi < v) ? hi : v;
102     }
103 
104     /* VolumeShaper.Configuration derives from the Interpolator class and adds
105      * parameters relating to the volume shape.
106      *
107      * This parallels the Java implementation and the enums must match.
108      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
109      * details on the Java implementation.
110      */
111     class Configuration : public Interpolator<S, T>, public RefBase, public Parcelable {
112     public:
113         // Must match with VolumeShaper.java in frameworks/base.
114         enum Type : int32_t {
115             TYPE_ID,
116             TYPE_SCALE,
117         };
118 
toString(Type type)119         static std::string toString(Type type) {
120             switch (type) {
121                 case TYPE_ID: return "TYPE_ID";
122                 case TYPE_SCALE: return "TYPE_SCALE";
123                 default:
124                     return std::string("Unknown Type: ")
125                             .append(std::to_string(static_cast<int>(type)));
126             }
127         }
128 
129         // Must match with VolumeShaper.java in frameworks/base.
130         enum OptionFlag : int32_t {
131             OPTION_FLAG_NONE           = 0,
132             OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
133             OPTION_FLAG_CLOCK_TIME     = (1 << 1),
134 
135             OPTION_FLAG_ALL            = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
136         };
137 
toString(OptionFlag flag)138         static std::string toString(OptionFlag flag) {
139             std::string s;
140             for (const auto& flagPair : std::initializer_list<std::pair<OptionFlag, const char*>>{
141                     {OPTION_FLAG_VOLUME_IN_DBFS, "OPTION_FLAG_VOLUME_IN_DBFS"},
142                     {OPTION_FLAG_CLOCK_TIME, "OPTION_FLAG_CLOCK_TIME"},
143                 }) {
144                 if (flag & flagPair.first) {
145                     if (!s.empty()) {
146                         s.append("|");
147                     }
148                     s.append(flagPair.second);
149                 }
150             }
151             return s;
152         }
153 
154         // Bring from base class; must match with VolumeShaper.java in frameworks/base.
155         using InterpolatorType = Interpolator<S, T>::InterpolatorType;
156 
Configuration()157         Configuration()
158             : Interpolator<S, T>()
159             , RefBase()
160             , mType(TYPE_SCALE)
161             , mId(-1)
162             , mOptionFlags(OPTION_FLAG_NONE)
163             , mDurationMs(1000.) {
164         }
165 
Configuration(const Configuration & configuration)166         Configuration(const Configuration &configuration)
167             : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
168             , RefBase()
169             , mType(configuration.mType)
170             , mId(configuration.mId)
171             , mOptionFlags(configuration.mOptionFlags)
172             , mDurationMs(configuration.mDurationMs) {
173         }
174 
getType()175         Type getType() const {
176             return mType;
177         }
178 
setType(Type type)179         status_t setType(Type type) {
180             switch (type) {
181             case TYPE_ID:
182             case TYPE_SCALE:
183                 mType = type;
184                 return NO_ERROR;
185             default:
186                 ALOGE("invalid Type: %d", type);
187                 return BAD_VALUE;
188             }
189         }
190 
getOptionFlags()191         OptionFlag getOptionFlags() const {
192             return mOptionFlags;
193         }
194 
setOptionFlags(OptionFlag optionFlags)195         status_t setOptionFlags(OptionFlag optionFlags) {
196             if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
197                 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
198                 return BAD_VALUE;
199             }
200             mOptionFlags = optionFlags;
201             return NO_ERROR;
202         }
203 
getDurationMs()204         double getDurationMs() const {
205             return mDurationMs;
206         }
207 
setDurationMs(double durationMs)208         status_t setDurationMs(double durationMs) {
209             if (durationMs > 0.) {
210                 mDurationMs = durationMs;
211                 return NO_ERROR;
212             }
213             // zero, negative, or nan. These values not possible from Java.
214             return BAD_VALUE;
215         }
216 
getId()217         int32_t getId() const {
218             return mId;
219         }
220 
setId(int32_t id)221         void setId(int32_t id) {
222             // We permit a negative id here (representing invalid).
223             mId = id;
224         }
225 
226         /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
227          * and compensate for log dbFS volume as needed.
228          */
adjustVolume(T volume)229         T adjustVolume(T volume) const {
230             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
231                 const T out = powf(10.f, volume / 10.f);
232                 VS_LOG("in: %f  out: %f", volume, out);
233                 volume = out;
234             }
235             return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
236         }
237 
238         /* Check if the existing curve is valid.
239          */
checkCurve()240         status_t checkCurve() const {
241             if (mType == TYPE_ID) return NO_ERROR;
242             if (this->size() < 2) {
243                 ALOGE("curve must have at least 2 points");
244                 return BAD_VALUE;
245             }
246             if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
247                 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
248                 return BAD_VALUE;
249             }
250             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
251                 for (const auto &pt : *this) {
252                     if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
253                         ALOGE("positive volume dbFS");
254                         return BAD_VALUE;
255                     }
256                 }
257             } else {
258                 for (const auto &pt : *this) {
259                     if (!(pt.second >= MIN_LINEAR_VOLUME)
260                             || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
261                         ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
262                         return BAD_VALUE;
263                     }
264                 }
265             }
266             return NO_ERROR;
267         }
268 
269         /* Clamps the volume curve in the configuration to
270          * the valid range for log or linear scale.
271          */
clampVolume()272         void clampVolume() {
273             if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
274                 for (auto it = this->begin(); it != this->end(); ++it) {
275                     if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
276                         it->second = MAX_LOG_VOLUME;
277                     }
278                 }
279             } else {
280                 for (auto it = this->begin(); it != this->end(); ++it) {
281                     if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
282                         it->second = MIN_LINEAR_VOLUME;
283                     } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
284                         it->second = MAX_LINEAR_VOLUME;
285                     }
286                 }
287             }
288         }
289 
290         /* scaleToStartVolume() is used to set the start volume of a
291          * new VolumeShaper curve, when replacing one VolumeShaper
292          * with another using the "join" (volume match) option.
293          *
294          * It works best for monotonic volume ramps or ducks.
295          */
scaleToStartVolume(T volume)296         void scaleToStartVolume(T volume) {
297             if (this->size() < 2) {
298                 return;
299             }
300             const T startVolume = first().second;
301             const T endVolume = last().second;
302             if (endVolume == startVolume) {
303                 // match with linear ramp
304                 const T offset = volume - startVolume;
305                 static const T scale =  1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
306                 for (auto it = this->begin(); it != this->end(); ++it) {
307                     it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
308                 }
309             } else {
310                 const T  scale = (volume - endVolume) / (startVolume - endVolume);
311                 for (auto it = this->begin(); it != this->end(); ++it) {
312                     it->second = scale * (it->second - endVolume) + endVolume;
313                 }
314             }
315             clampVolume();
316         }
317 
writeToParcel(Parcel * parcel)318         status_t writeToParcel(Parcel *parcel) const override {
319             VolumeShaperConfiguration parcelable;
320             writeToParcelable(&parcelable);
321             return parcelable.writeToParcel(parcel);
322         }
323 
writeToParcelable(VolumeShaperConfiguration * parcelable)324         void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
325             parcelable->id = getId();
326             parcelable->type = getTypeAsAidl();
327             parcelable->optionFlags = 0;
328             if (mType != TYPE_ID) {
329                 parcelable->optionFlags = getOptionFlagsAsAidl();
330                 parcelable->durationMs = getDurationMs();
331                 parcelable->interpolatorConfig.emplace(); // create value in std::optional
332                 Interpolator<S, T>::writeToConfig(&*parcelable->interpolatorConfig);
333             }
334         }
335 
readFromParcel(const Parcel * parcel)336         status_t readFromParcel(const Parcel* parcel) override {
337             VolumeShaperConfiguration data;
338             return data.readFromParcel(parcel)
339                    ?: readFromParcelable(data);
340         }
341 
readFromParcelable(const VolumeShaperConfiguration & parcelable)342         status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
343             setId(parcelable.id);
344             return setTypeFromAidl(parcelable.type)
345                    ?: mType == TYPE_ID
346                       ? NO_ERROR
347                       : setOptionFlagsFromAidl(parcelable.optionFlags)
348                         ?: setDurationMs(parcelable.durationMs)
349                            ?: !parcelable.interpolatorConfig  // check std::optional for value
350                                ? BAD_VALUE // must be nonnull.
351                                : Interpolator<S, T>::readFromConfig(*parcelable.interpolatorConfig)
352                                    ?: checkCurve();
353         }
354 
355         // Returns a string for debug printing.
toString()356         std::string toString() const {
357             std::stringstream ss;
358             ss << "VolumeShaper::Configuration{mType=" << toString(mType);
359             ss << ", mId=" << mId;
360             if (mType != TYPE_ID) {
361                 ss << ", mOptionFlags=" << toString(mOptionFlags);
362                 ss << ", mDurationMs=" << mDurationMs;
363                 ss << ", " << Interpolator<S, T>::toString().c_str();
364             }
365             ss << "}";
366             return ss.str();
367         }
368 
369     private:
370         Type mType;              // type of configuration
371         int32_t mId;             // A valid id is >= 0.
372         OptionFlag mOptionFlags; // option flags for the configuration.
373         double mDurationMs;      // duration, must be > 0; default is 1000 ms.
374 
getOptionFlagsAsAidl()375         int32_t getOptionFlagsAsAidl() const {
376             int32_t result = 0;
377             if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
378                 result |=
379                         1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
380             }
381             if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
382                 result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
383             }
384             return result;
385         }
386 
setOptionFlagsFromAidl(int32_t aidl)387         status_t setOptionFlagsFromAidl(int32_t aidl) {
388             std::underlying_type_t<OptionFlag> options = 0;
389             if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
390                 options |= OPTION_FLAG_VOLUME_IN_DBFS;
391             }
392             if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
393                 options |= OPTION_FLAG_CLOCK_TIME;
394             }
395             return setOptionFlags(static_cast<OptionFlag>(options));
396         }
397 
setTypeFromAidl(VolumeShaperConfigurationType aidl)398         status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
399             switch (aidl) {
400                 case VolumeShaperConfigurationType::ID:
401                     return setType(TYPE_ID);
402                 case VolumeShaperConfigurationType::SCALE:
403                     return setType(TYPE_SCALE);
404                 default:
405                     return BAD_VALUE;
406             }
407         }
408 
getTypeAsAidl()409         VolumeShaperConfigurationType getTypeAsAidl() const {
410             switch (getType()) {
411                 case TYPE_ID:
412                     return VolumeShaperConfigurationType::ID;
413                 case TYPE_SCALE:
414                     return VolumeShaperConfigurationType::SCALE;
415                 default:
416                     LOG_ALWAYS_FATAL("Shouldn't get here");
417             }
418         }
419     }; // Configuration
420 
421     /* VolumeShaper::Operation expresses an operation to perform on the
422      * configuration (either explicitly specified or an id).
423      *
424      * This parallels the Java implementation and the enums must match.
425      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
426      * details on the Java implementation.
427      */
428     class Operation : public RefBase, public Parcelable {
429     public:
430         // Must match with VolumeShaper.java.
431         enum Flag : int32_t {
432             FLAG_NONE      = 0,
433             FLAG_REVERSE   = (1 << 0), // the absence of this indicates "play"
434             FLAG_TERMINATE = (1 << 1),
435             FLAG_JOIN      = (1 << 2),
436             FLAG_DELAY     = (1 << 3),
437             FLAG_CREATE_IF_NECESSARY = (1 << 4),
438 
439             FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
440                             | FLAG_CREATE_IF_NECESSARY),
441         };
442 
toString(Flag flag)443         static std::string toString(Flag flag) {
444             std::string s;
445             for (const auto& flagPair : std::initializer_list<std::pair<Flag, const char*>>{
446                     {FLAG_REVERSE, "FLAG_REVERSE"},
447                     {FLAG_TERMINATE, "FLAG_TERMINATE"},
448                     {FLAG_JOIN, "FLAG_JOIN"},
449                     {FLAG_DELAY, "FLAG_DELAY"},
450                     {FLAG_CREATE_IF_NECESSARY, "FLAG_CREATE_IF_NECESSARY"},
451                 }) {
452                 if (flag & flagPair.first) {
453                     if (!s.empty()) {
454                         s.append("|");
455                     }
456                     s.append(flagPair.second);
457                 }
458             }
459             return s;
460         }
461 
Operation()462         Operation()
463             : Operation(FLAG_NONE, -1 /* replaceId */) {
464         }
465 
Operation(Flag flags,int replaceId)466         Operation(Flag flags, int replaceId)
467             : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
468         }
469 
Operation(const Operation & operation)470         Operation(const Operation &operation)
471             : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
472         }
473 
Operation(const sp<Operation> & operation)474         explicit Operation(const sp<Operation> &operation)
475             : Operation(*operation.get()) {
476         }
477 
Operation(Flag flags,int replaceId,S xOffset)478         Operation(Flag flags, int replaceId, S xOffset)
479             : mFlags(flags)
480             , mReplaceId(replaceId)
481             , mXOffset(xOffset) {
482         }
483 
getReplaceId()484         int32_t getReplaceId() const {
485             return mReplaceId;
486         }
487 
setReplaceId(int32_t replaceId)488         void setReplaceId(int32_t replaceId) {
489             mReplaceId = replaceId;
490         }
491 
getXOffset()492         S getXOffset() const {
493             return mXOffset;
494         }
495 
setXOffset(S xOffset)496         void setXOffset(S xOffset) {
497             mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
498         }
499 
getFlags()500         Flag getFlags() const {
501             return mFlags;
502         }
503 
504         /* xOffset is the position on the volume curve and may go backwards
505          * if you are in reverse mode. This must be in the range from
506          * [MIN_CURVE_TIME, MAX_CURVE_TIME].
507          *
508          * normalizedTime always increases as time or framecount increases.
509          * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
510          * running through the curve, but could be outside this range afterwards.
511          * If you are reversing, this means the position on the curve, or xOffset,
512          * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
513          * [MIN_CURVE_TIME, MAX_CURVE_TIME].
514          */
setNormalizedTime(S normalizedTime)515         void setNormalizedTime(S normalizedTime) {
516             setXOffset((mFlags & FLAG_REVERSE) != 0
517                     ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
518         }
519 
setFlags(Flag flags)520         status_t setFlags(Flag flags) {
521             if ((flags & ~FLAG_ALL) != 0) {
522                 ALOGE("flags has invalid bits: %#x", flags);
523                 return BAD_VALUE;
524             }
525             mFlags = flags;
526             return NO_ERROR;
527         }
528 
writeToParcel(Parcel * parcel)529         status_t writeToParcel(Parcel* parcel) const override {
530             if (parcel == nullptr) return BAD_VALUE;
531             VolumeShaperOperation op;
532             writeToParcelable(&op);
533             return op.writeToParcel(parcel);
534         }
535 
writeToParcelable(VolumeShaperOperation * op)536         void writeToParcelable(VolumeShaperOperation* op) const {
537             op->flags = getFlagsAsAidl();
538             op->replaceId = mReplaceId;
539             op->xOffset = mXOffset;
540         }
541 
readFromParcel(const Parcel * parcel)542         status_t readFromParcel(const Parcel* parcel) override {
543             VolumeShaperOperation op;
544             return op.readFromParcel(parcel)
545                    ?: readFromParcelable(op);
546         }
547 
readFromParcelable(const VolumeShaperOperation & op)548         status_t readFromParcelable(const VolumeShaperOperation& op) {
549             mReplaceId = op.replaceId;
550             mXOffset = op.xOffset;
551             return setFlagsFromAidl(op.flags);
552         }
553 
toString()554         std::string toString() const {
555             std::stringstream ss;
556             ss << "VolumeShaper::Operation{mFlags=" << toString(mFlags);
557             ss << ", mReplaceId=" << mReplaceId;
558             ss << ", mXOffset=" << mXOffset;
559             ss << "}";
560             return ss.str();
561         }
562 
563     private:
setFlagsFromAidl(int32_t aidl)564         status_t setFlagsFromAidl(int32_t aidl) {
565             std::underlying_type_t<Flag> flags = 0;
566             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
567                 flags |= FLAG_REVERSE;
568             }
569             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
570                 flags |= FLAG_TERMINATE;
571             }
572             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
573                 flags |= FLAG_JOIN;
574             }
575             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
576                 flags |= FLAG_DELAY;
577             }
578             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
579                 flags |= FLAG_CREATE_IF_NECESSARY;
580             }
581             return setFlags(static_cast<Flag>(flags));
582         }
583 
getFlagsAsAidl()584         int32_t getFlagsAsAidl() const {
585             int32_t aidl = 0;
586             std::underlying_type_t<Flag> flags = getFlags();
587             if (flags & FLAG_REVERSE) {
588                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
589             }
590             if (flags & FLAG_TERMINATE) {
591                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
592             }
593             if (flags & FLAG_JOIN) {
594                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
595             }
596             if (flags & FLAG_DELAY) {
597                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
598             }
599             if (flags & FLAG_CREATE_IF_NECESSARY) {
600                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
601             }
602             return aidl;
603         }
604 
605     private:
606         Flag mFlags;        // operation to do
607         int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
608         S mXOffset;         // position in the curve to set if a valid number (not nan)
609     }; // Operation
610 
611     /* VolumeShaper.State is returned when requesting the last
612      * state of the VolumeShaper.
613      *
614      * This parallels the Java implementation.
615      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
616      * details on the Java implementation.
617      */
618     class State : public RefBase, public Parcelable {
619     public:
State(T volume,S xOffset)620         State(T volume, S xOffset)
621             : mVolume(volume)
622             , mXOffset(xOffset) {
623         }
624 
State()625         State()
626             : State(NAN, NAN) { }
627 
getVolume()628         T getVolume() const {
629             return mVolume;
630         }
631 
setVolume(T volume)632         void setVolume(T volume) {
633             mVolume = volume;
634         }
635 
getXOffset()636         S getXOffset() const {
637             return mXOffset;
638         }
639 
setXOffset(S xOffset)640         void setXOffset(S xOffset) {
641             mXOffset = xOffset;
642         }
643 
writeToParcel(Parcel * parcel)644         status_t writeToParcel(Parcel* parcel) const override {
645             if (parcel == nullptr) return BAD_VALUE;
646             VolumeShaperState state;
647             writeToParcelable(&state);
648             return state.writeToParcel(parcel);
649         }
650 
writeToParcelable(VolumeShaperState * parcelable)651         void writeToParcelable(VolumeShaperState* parcelable) const {
652             parcelable->volume = mVolume;
653             parcelable->xOffset = mXOffset;
654         }
655 
readFromParcel(const Parcel * parcel)656         status_t readFromParcel(const Parcel* parcel) override {
657             VolumeShaperState state;
658             return state.readFromParcel(parcel)
659                    ?: readFromParcelable(state);
660         }
661 
readFromParcelable(const VolumeShaperState & parcelable)662         status_t readFromParcelable(const VolumeShaperState& parcelable) {
663             mVolume = parcelable.volume;
664             mXOffset = parcelable.xOffset;
665             return OK;
666         }
667 
toString()668         std::string toString() const {
669             std::stringstream ss;
670             ss << "VolumeShaper::State{mVolume=" << mVolume;
671             ss << ", mXOffset=" << mXOffset;
672             ss << "}";
673             return ss.str();
674         }
675 
676     private:
677         T mVolume;   // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
678         S mXOffset;  // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
679     }; // State
680 
681     // Internal helper class to do an affine transform for time and amplitude scaling.
682     template <typename R>
683     class Translate {
684     public:
Translate()685         Translate()
686             : mOffset(0)
687             , mScale(1) {
688         }
689 
getOffset()690         R getOffset() const {
691             return mOffset;
692         }
693 
setOffset(R offset)694         void setOffset(R offset) {
695             mOffset = offset;
696         }
697 
getScale()698         R getScale() const {
699             return mScale;
700         }
701 
setScale(R scale)702         void setScale(R scale) {
703             mScale = scale;
704         }
705 
operator()706         R operator()(R in) const {
707             return mScale * (in - mOffset);
708         }
709 
toString()710         std::string toString() const {
711             std::stringstream ss;
712             ss << "VolumeShaper::Translate{mOffset=" << mOffset;
713             ss << ", mScale=" << mScale;
714             ss << "}";
715             return ss.str();
716         }
717 
718     private:
719         R mOffset;
720         R mScale;
721     }; // Translate
722 
convertTimespecToUs(const struct timespec & tv)723     static int64_t convertTimespecToUs(const struct timespec &tv)
724     {
725         return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
726     }
727 
728     // current monotonic time in microseconds.
getNowUs()729     static int64_t getNowUs()
730     {
731         struct timespec tv;
732         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
733             return 0; // system is really sick, just return 0 for consistency.
734         }
735         return convertTimespecToUs(tv);
736     }
737 
738     /* Native implementation of VolumeShaper.  This is NOT mirrored
739      * on the Java side, so we don't need to mimic Java side layout
740      * and data; furthermore, this isn't refcounted as a "RefBase" object.
741      *
742      * Since we pass configuration and operation as shared pointers (like
743      * Java) there is a potential risk that the caller may modify
744      * these after delivery.
745      */
VolumeShaper(const sp<VolumeShaper::Configuration> & configuration,const sp<VolumeShaper::Operation> & operation)746     VolumeShaper(
747             const sp<VolumeShaper::Configuration> &configuration,
748             const sp<VolumeShaper::Operation> &operation)
749         : mConfiguration(configuration) // we do not make a copy
750         , mOperation(operation)         // ditto
751         , mStartFrame(-1)
752         , mLastVolume(T(1))
753         , mLastXOffset(MIN_CURVE_TIME)
754         , mDelayXOffset(MIN_CURVE_TIME) {
755         if (configuration.get() != nullptr
756                 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
757             mLastVolume = configuration->first().second;
758         }
759     }
760 
761     // We allow a null operation here, though VolumeHandler always provides one.
getFlags()762     VolumeShaper::Operation::Flag getFlags() const {
763         return mOperation == nullptr
764                 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
765     }
766 
767     /* Returns the last volume and xoffset reported to the AudioFlinger.
768      * If the VolumeShaper has not been started, compute what the volume
769      * should be based on the initial offset specified.
770      */
getState()771     sp<VolumeShaper::State> getState() const {
772         if (!isStarted()) {
773             const T volume = computeVolumeFromXOffset(mDelayXOffset);
774             VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
775                     mDelayXOffset, volume);
776             return new VolumeShaper::State(volume, mDelayXOffset);
777         } else {
778             return new VolumeShaper::State(mLastVolume, mLastXOffset);
779         }
780     }
781 
getDelayXOffset()782     S getDelayXOffset() const {
783         return mDelayXOffset;
784     }
785 
setDelayXOffset(S xOffset)786     void setDelayXOffset(S xOffset) {
787         mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
788     }
789 
isStarted()790     bool isStarted() const {
791         return mStartFrame >= 0;
792     }
793 
794     /* getVolume() updates the last volume/xoffset state so it is not
795      * const, even though logically it may be viewed as const.
796      */
getVolume(int64_t trackFrameCount,double trackSampleRate)797     std::pair<T /* volume */, bool /* active */> getVolume(
798             int64_t trackFrameCount, double trackSampleRate) {
799         if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
800             // We haven't had PLAY called yet, so just return the value
801             // as if PLAY were called just now.
802             VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
803             const T volume = computeVolumeFromXOffset(mDelayXOffset);
804             return std::make_pair(volume, false);
805         }
806         const bool clockTime = (mConfiguration->getOptionFlags()
807                 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
808         const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
809         const double sampleRate = clockTime ? 1000000 : trackSampleRate;
810 
811         if (mStartFrame < 0) {
812             updatePosition(frameCount, sampleRate, mDelayXOffset);
813             mStartFrame = frameCount;
814         }
815         VS_LOG("frameCount: %lld", (long long)frameCount);
816         const S x = mXTranslate((T)frameCount);
817         VS_LOG("translation to normalized time: %f", x);
818 
819         std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
820                 computeStateFromNormalizedTime(x);
821 
822         mLastVolume = std::get<0>(vt);
823         mLastXOffset = std::get<1>(vt);
824         const bool active = std::get<2>(vt);
825         VS_LOG("rescaled time:%f  volume:%f  xOffset:%f  active:%s",
826                 x, mLastVolume, mLastXOffset, active ? "true" : "false");
827         return std::make_pair(mLastVolume, active);
828     }
829 
toString()830     std::string toString() const {
831         std::stringstream ss;
832         ss << "VolumeShaper{mStartFrame=" << mStartFrame;
833         ss << ", mXTranslate=" << mXTranslate.toString().c_str();
834         ss << ", mConfiguration=" <<
835                 (mConfiguration.get() == nullptr
836                         ? "nullptr" : mConfiguration->toString().c_str());
837         ss << ", mOperation=" <<
838                 (mOperation.get() == nullptr
839                         ? "nullptr" :  mOperation->toString().c_str());
840         ss << "}";
841         return ss.str();
842     }
843 
844     Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
845     sp<VolumeShaper::Configuration> mConfiguration;
846     sp<VolumeShaper::Operation> mOperation;
847 
848 private:
849     int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
850     T mLastVolume;       // last computed interpolated volume (y-axis)
851     S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
852     S mDelayXOffset;     // xOffset to use for first invocation of VolumeShaper.
853 
854     // Called internally to adjust mXTranslate for first time start.
updatePosition(int64_t startFrame,double sampleRate,S xOffset)855     void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
856         double scale = (mConfiguration->last().first - mConfiguration->first().first)
857                         / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
858         const double minScale = 1. / static_cast<double>(INT64_MAX);
859         scale = std::max(scale, minScale);
860         VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
861                 scale, (long long) startFrame, sampleRate, xOffset);
862 
863         S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
864                 MAX_CURVE_TIME - xOffset : xOffset;
865         mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
866                                                  - static_cast<double>(normalizedTime) / scale));
867         mXTranslate.setScale(static_cast<float>(scale));
868         VS_LOG("translate: %s", mXTranslate.toString().c_str());
869     }
870 
computeVolumeFromXOffset(S xOffset)871     T computeVolumeFromXOffset(S xOffset) const {
872         const T unscaledVolume = mConfiguration->findY(xOffset);
873         const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
874         VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
875         return volume;
876     }
877 
878     std::tuple<T /* volume */, S /* position */, bool /* active */>
computeStateFromNormalizedTime(S x)879     computeStateFromNormalizedTime(S x) const {
880         bool active = true;
881         // handle reversal of position
882         if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
883             x = MAX_CURVE_TIME - x;
884             VS_LOG("reversing to %f", x);
885             if (x < MIN_CURVE_TIME) {
886                 x = MIN_CURVE_TIME;
887                 active = false; // at the end
888             } else if (x > MAX_CURVE_TIME) {
889                 x = MAX_CURVE_TIME; //early
890             }
891         } else {
892             if (x < MIN_CURVE_TIME) {
893                 x = MIN_CURVE_TIME; // early
894             } else if (x > MAX_CURVE_TIME) {
895                 x = MAX_CURVE_TIME;
896                 active = false; // at end
897             }
898         }
899         const S xOffset = x;
900         const T volume = computeVolumeFromXOffset(xOffset);
901         return std::make_tuple(volume, xOffset, active);
902     }
903 }; // VolumeShaper
904 
905 /* VolumeHandler combines the volume factors of multiple VolumeShapers associated
906  * with a player.  It is thread safe by synchronizing all public methods.
907  *
908  * This is a native-only implementation.
909  *
910  * The server side VolumeHandler is used to maintain a list of volume handlers,
911  * keep state, and obtain volume.
912  *
913  * The client side VolumeHandler is used to maintain a list of volume handlers,
914  * keep some partial state, and restore if the server dies.
915  */
916 class VolumeHandler : public RefBase {
917 public:
918     using S = float;
919     using T = float;
920 
921     // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
VolumeHandler()922     VolumeHandler()
923         : VolumeHandler(0 /* sampleRate */) {
924     }
925 
VolumeHandler(uint32_t sampleRate)926     explicit VolumeHandler(uint32_t sampleRate)
927         : mSampleRate((double)sampleRate)
928         , mLastFrame(0)
929         , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
930         , mLastVolume(1.f, false) {
931     }
932 
applyVolumeShaper(const sp<VolumeShaper::Configuration> & configuration,const sp<VolumeShaper::Operation> & operation_in)933     VolumeShaper::Status applyVolumeShaper(
934             const sp<VolumeShaper::Configuration> &configuration,
935             const sp<VolumeShaper::Operation> &operation_in) {
936         // make a local copy of operation, as we modify it.
937         sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
938         VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
939         VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
940         AutoMutex _l(mLock);
941         if (configuration == nullptr) {
942             ALOGE("null configuration");
943             return VolumeShaper::Status(BAD_VALUE);
944         }
945         if (operation == nullptr) {
946             ALOGE("null operation");
947             return VolumeShaper::Status(BAD_VALUE);
948         }
949         const int32_t id = configuration->getId();
950         if (id < 0) {
951             ALOGE("negative id: %d", id);
952             return VolumeShaper::Status(BAD_VALUE);
953         }
954         VS_LOG("applyVolumeShaper id: %d", id);
955 
956         switch (configuration->getType()) {
957         case VolumeShaper::Configuration::TYPE_SCALE: {
958             const int replaceId = operation->getReplaceId();
959             if (replaceId >= 0) {
960                 VS_LOG("replacing %d", replaceId);
961                 auto replaceIt = findId_l(replaceId);
962                 if (replaceIt == mVolumeShapers.end()) {
963                     ALOGW("cannot find replace id: %d", replaceId);
964                 } else {
965                     if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
966                         // For join, we scale the start volume of the current configuration
967                         // to match the last-used volume of the replacing VolumeShaper.
968                         auto state = replaceIt->getState();
969                         ALOGD("join: state:%s", state->toString().c_str());
970                         if (state->getXOffset() >= 0) { // valid
971                             const T volume = state->getVolume();
972                             ALOGD("join: scaling start volume to %f", volume);
973                             configuration->scaleToStartVolume(volume);
974                         }
975                     }
976                     (void)mVolumeShapers.erase(replaceIt);
977                 }
978                 operation->setReplaceId(-1);
979             }
980             // check if we have another of the same id.
981             auto oldIt = findId_l(id);
982             if (oldIt != mVolumeShapers.end()) {
983                 if ((operation->getFlags()
984                         & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
985                     // TODO: move the case to a separate function.
986                     goto HANDLE_TYPE_ID; // no need to create, take over existing id.
987                 }
988                 ALOGW("duplicate id, removing old %d", id);
989                 (void)mVolumeShapers.erase(oldIt);
990             }
991 
992             /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
993              * We check on the server side to ensure synchronization and robustness.
994              *
995              * This shouldn't fail on a replace command unless the replaced id is
996              * already invalid (which *should* be checked in the Java layer).
997              */
998             if (id >= VolumeShaper::kSystemVolumeShapersMax
999                     && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
1000                 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
1001                 return VolumeShaper::Status(INVALID_OPERATION);
1002             }
1003 
1004             // create new VolumeShaper with default behavior.
1005             mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
1006             VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
1007         }
1008         // fall through to handle the operation
1009         HANDLE_TYPE_ID:
1010         case VolumeShaper::Configuration::TYPE_ID: {
1011             VS_LOG("trying to find id: %d", id);
1012             auto it = findId_l(id);
1013             if (it == mVolumeShapers.end()) {
1014                 VS_LOG("couldn't find id: %d", id);
1015                 return VolumeShaper::Status(INVALID_OPERATION);
1016             }
1017             if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
1018                 VS_LOG("terminate id: %d", id);
1019                 mVolumeShapers.erase(it);
1020                 break;
1021             }
1022             const bool clockTime = (it->mConfiguration->getOptionFlags()
1023                     & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
1024             if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
1025                     (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
1026                 if (it->isStarted()) {
1027                     const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1028                     const S x = it->mXTranslate((T)frameCount);
1029                     VS_LOG("reverse normalizedTime: %f", x);
1030                     // reflect position
1031                     S target = MAX_CURVE_TIME - x;
1032                     if (target < MIN_CURVE_TIME) {
1033                         VS_LOG("clamp to start - begin immediately");
1034                         target = MIN_CURVE_TIME;
1035                     }
1036                     VS_LOG("reverse normalizedTime target: %f", target);
1037                     it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1038                             + (x - target) / it->mXTranslate.getScale());
1039                 }
1040                 // if not started, the delay offset doesn't change.
1041             }
1042             const S xOffset = operation->getXOffset();
1043             if (!std::isnan(xOffset)) {
1044                 if (it->isStarted()) {
1045                     const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1046                     const S x = it->mXTranslate((T)frameCount);
1047                     VS_LOG("normalizedTime translation: %f", x);
1048                     const S target =
1049                             (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
1050                                     MAX_CURVE_TIME - xOffset : xOffset;
1051                     VS_LOG("normalizedTime target x offset: %f", target);
1052                     it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1053                             + (x - target) / it->mXTranslate.getScale());
1054                 } else {
1055                     it->setDelayXOffset(xOffset);
1056                 }
1057             }
1058             it->mOperation = operation; // replace the operation
1059         } break;
1060         }
1061         return VolumeShaper::Status(id);
1062     }
1063 
getVolumeShaperState(int id)1064     sp<VolumeShaper::State> getVolumeShaperState(int id) {
1065         AutoMutex _l(mLock);
1066         auto it = findId_l(id);
1067         if (it == mVolumeShapers.end()) {
1068             VS_LOG("cannot find state for id: %d", id);
1069             return nullptr;
1070         }
1071         return it->getState();
1072     }
1073 
1074     /* getVolume() is not const, as it updates internal state.
1075      * Once called, any VolumeShapers not already started begin running.
1076      */
getVolume(int64_t trackFrameCount)1077     std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
1078         AutoMutex _l(mLock);
1079         mLastFrame = trackFrameCount;
1080         T volume(1);
1081         size_t activeCount = 0;
1082         for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
1083             const std::pair<T, bool> shaperVolume =
1084                     it->getVolume(trackFrameCount, mSampleRate);
1085             volume *= shaperVolume.first;
1086             activeCount += shaperVolume.second;
1087             ++it;
1088         }
1089         mLastVolume = std::make_pair(volume, activeCount != 0);
1090         VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
1091         return mLastVolume;
1092     }
1093 
1094     /* Used by a client side VolumeHandler to ensure all the VolumeShapers
1095      * indicate that they have been started.  Upon a change in audioserver
1096      * output sink, this information is used for restoration of the server side
1097      * VolumeHandler.
1098      */
setStarted()1099     void setStarted() {
1100         (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
1101     }
1102 
getLastVolume()1103     std::pair<T /* volume */, bool /* active */> getLastVolume() const {
1104         AutoMutex _l(mLock);
1105         return mLastVolume;
1106     }
1107 
toString()1108     std::string toString() const {
1109         AutoMutex _l(mLock);
1110         std::stringstream ss;
1111         ss << "VolumeHandler{mSampleRate=" << mSampleRate;
1112         ss << ", mLastFrame=" << mLastFrame;
1113         ss << ", mVolumeShapers={";
1114         bool first = true;
1115         for (const auto &shaper : mVolumeShapers) {
1116             if (first) {
1117                 first = false;
1118             } else {
1119                 ss << ", ";
1120             }
1121             ss << shaper.toString().c_str();
1122         }
1123         ss << "}}";
1124         return ss.str();
1125     }
1126 
forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> & lambda)1127     void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
1128         AutoMutex _l(mLock);
1129         VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
1130         for (const auto &shaper : mVolumeShapers) {
1131             VolumeShaper::Status status = lambda(shaper);
1132             VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
1133         }
1134     }
1135 
reset()1136     void reset() {
1137         AutoMutex _l(mLock);
1138         mVolumeShapers.clear();
1139         mLastFrame = 0;
1140         // keep mVolumeShaperIdCounter as is.
1141     }
1142 
1143     /* Sets the configuration id if necessary - This is based on the counter
1144      * internal to the VolumeHandler.
1145      */
setIdIfNecessary(const sp<VolumeShaper::Configuration> & configuration)1146     void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
1147         if (configuration && configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
1148             const int id = configuration->getId();
1149             if (id == -1) {
1150                 // Reassign to a unique id, skipping system ids.
1151                 AutoMutex _l(mLock);
1152                 while (true) {
1153                     if (mVolumeShaperIdCounter == INT32_MAX) {
1154                         mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
1155                     } else {
1156                         ++mVolumeShaperIdCounter;
1157                     }
1158                     if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
1159                         continue; // collision with an existing id.
1160                     }
1161                     configuration->setId(mVolumeShaperIdCounter);
1162                     ALOGD("setting id to %d", mVolumeShaperIdCounter);
1163                     break;
1164                 }
1165             }
1166         }
1167     }
1168 
1169 private:
findId_l(int32_t id)1170     std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1171         std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1172         for (; it != mVolumeShapers.end(); ++it) {
1173             if (it->mConfiguration->getId() == id) {
1174                 break;
1175             }
1176         }
1177         return it;
1178     }
1179 
numberOfUserVolumeShapers_l()1180     size_t numberOfUserVolumeShapers_l() const {
1181         size_t count = 0;
1182         for (const auto &shaper : mVolumeShapers) {
1183             count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1184         }
1185         return count;
1186     }
1187 
1188     mutable Mutex mLock;
1189     double mSampleRate; // in samples (frames) per second
1190     int64_t mLastFrame; // logging purpose only, 0 on start
1191     int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
1192     std::pair<T /* volume */, bool /* active */> mLastVolume;
1193     std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1194 }; // VolumeHandler
1195 
1196 } // namespace media
1197 
1198 } // namespace android
1199 
1200 #pragma pop_macro("LOG_TAG")
1201 
1202 #endif // ANDROID_VOLUME_SHAPER_H
1203