• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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_AUDIO_TRACKMETRICS_H
18 #define ANDROID_AUDIO_TRACKMETRICS_H
19 
20 #include <mutex>
21 
22 namespace android {
23 
24 /**
25  * TrackMetrics handles the AudioFlinger track metrics.
26  *
27  * We aggregate metrics for a particular device for proper analysis.
28  * This includes power, performance, and usage metrics.
29  *
30  * This class is thread-safe with a lock for safety.  There is no risk of deadlock
31  * as this class only executes external one-way calls in Mediametrics and does not
32  * call any other AudioFlinger class.
33  *
34  * Terminology:
35  * An AudioInterval is a contiguous playback segment.
36  * An AudioIntervalGroup is a group of continuous playback segments on the same device.
37  *
38  * We currently deliver metrics based on an AudioIntervalGroup.
39  */
40 class TrackMetrics final {
41 public:
TrackMetrics(std::string metricsId,bool isOut)42     TrackMetrics(std::string metricsId, bool isOut)
43         : mMetricsId(std::move(metricsId))
44         , mIsOut(isOut)
45         {}  // we don't log a constructor item, we wait for more info in logConstructor().
46 
~TrackMetrics()47     ~TrackMetrics() {
48         logEndInterval();
49         std::lock_guard l(mLock);
50         deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
51         // we don't log a destructor item here.
52     }
53 
54     // Called under the following circumstances
55     // 1) when we are added to the Thread
56     // 2) when we have a createPatch in the Thread.
logBeginInterval(const std::string & devices)57     void logBeginInterval(const std::string& devices) {
58         std::lock_guard l(mLock);
59         if (mDevices != devices) {
60             deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
61             mDevices = devices;
62             resetIntervalGroupMetrics();
63             deliverDeviceMetrics(
64                     AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str());
65         }
66         ++mIntervalCount;
67     }
68 
69     void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
70             const std::string& traits = {},
71             audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
72         // Once this item is logged by the server, the client can add properties.
73         // no lock required, all local or const variables.
74         mediametrics::LogItem item(mMetricsId);
75         item.setPid(creatorPid)
76             .setUid(creatorUid)
77             .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
78             .set(AMEDIAMETRICS_PROP_EVENT,
79                     AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
80             .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId)
81             .set(AMEDIAMETRICS_PROP_TRAITS, traits);
82         // log streamType from the service, since client doesn't know chosen streamType.
83         if (streamType != AUDIO_STREAM_DEFAULT) {
84             item.set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(streamType).c_str());
85         }
86         item.record();
87     }
88 
89     // Called when we are removed from the Thread.
logEndInterval()90     void logEndInterval() {
91         std::lock_guard l(mLock);
92         if (mLastVolumeChangeTimeNs != 0) {
93             logVolume_l(mVolume); // flush out the last volume.
94             mLastVolumeChangeTimeNs = 0;
95         }
96     }
97 
logInvalidate()98     void logInvalidate() const {
99         // no lock required, all local or const variables.
100         mediametrics::LogItem(mMetricsId)
101             .set(AMEDIAMETRICS_PROP_EVENT,
102                  AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE)
103             .record();
104     }
105 
logLatencyAndStartup(double latencyMs,double startupMs)106     void logLatencyAndStartup(double latencyMs, double startupMs) {
107         mediametrics::LogItem(mMetricsId)
108             .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
109             .set(AMEDIAMETRICS_PROP_STARTUPMS, startupMs)
110             .record();
111         std::lock_guard l(mLock);
112         mDeviceLatencyMs.add(latencyMs);
113         mDeviceStartupMs.add(startupMs);
114     }
115 
updateMinMaxVolume(int64_t durationNs,double deviceVolume)116     void updateMinMaxVolume(int64_t durationNs, double deviceVolume) {
117         if (deviceVolume > mMaxVolume) {
118             mMaxVolume = deviceVolume;
119             mMaxVolumeDurationNs = durationNs;
120         } else if (deviceVolume == mMaxVolume) {
121             mMaxVolumeDurationNs += durationNs;
122         }
123         if (deviceVolume < mMinVolume) {
124             mMinVolume = deviceVolume;
125             mMinVolumeDurationNs = durationNs;
126         } else if (deviceVolume == mMinVolume) {
127             mMinVolumeDurationNs += durationNs;
128         }
129     }
130 
131     // may be called multiple times during an interval
logVolume(float volume)132     void logVolume(float volume) {
133         std::lock_guard l(mLock);
134         logVolume_l(volume);
135     }
136 
137     // Use absolute numbers returned by AudioTrackShared.
logUnderruns(size_t count,size_t frames)138     void logUnderruns(size_t count, size_t frames) {
139         std::lock_guard l(mLock);
140         mUnderrunCount = count;
141         mUnderrunFrames = frames;
142         // Consider delivering a message here (also be aware of excessive spam).
143     }
144 
145 private:
146 
147     // no lock required - all arguments and constants.
deliverDeviceMetrics(const char * eventName,const char * devices)148     void deliverDeviceMetrics(const char *eventName, const char *devices) const {
149         mediametrics::LogItem(mMetricsId)
150             .set(AMEDIAMETRICS_PROP_EVENT, eventName)
151             .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
152                    : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
153            .record();
154     }
155 
logVolume_l(float volume)156     void logVolume_l(float volume) REQUIRES(mLock) {
157         const int64_t timeNs = systemTime();
158         const int64_t durationNs = mLastVolumeChangeTimeNs == 0
159                 ? 0 : timeNs - mLastVolumeChangeTimeNs;
160         if (durationNs > 0) {
161             // See West's algorithm for weighted averages
162             // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
163             mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
164                       / (durationNs + mDeviceTimeNs);
165             mDeviceTimeNs += durationNs;
166             mCumulativeTimeNs += durationNs;
167         }
168         updateMinMaxVolume(durationNs, mVolume); // always update.
169         mVolume = volume;
170         mLastVolumeChangeTimeNs = timeNs;
171     }
172 
deliverCumulativeMetrics(const char * eventName)173     void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
174         if (mIntervalCount > 0) {
175             mediametrics::LogItem item(mMetricsId);
176             item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
177                 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
178                 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
179                 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
180             if (mIsOut) {
181                 item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume)
182                     .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUMEDURATIONNS, mMaxVolumeDurationNs)
183                     .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUME, mMaxVolume)
184                     .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUMEDURATIONNS, mMinVolumeDurationNs)
185                     .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUME, mMinVolume);
186             }
187             if (mDeviceLatencyMs.getN() > 0) {
188                 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean())
189                     .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean());
190             }
191             if (mUnderrunCount > 0) {
192                 item.set(AMEDIAMETRICS_PROP_UNDERRUN,
193                         (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup))
194                     .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES,
195                         (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup));
196             }
197             item.record();
198         }
199     }
200 
resetIntervalGroupMetrics()201     void resetIntervalGroupMetrics() REQUIRES(mLock) {
202         // mDevices is not reset by resetIntervalGroupMetrics.
203 
204         mIntervalCount = 0;
205         // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
206         mDeviceTimeNs = 0;
207 
208         mVolume = 0.f;
209         mDeviceVolume = 0.f;
210         mLastVolumeChangeTimeNs = 0;  // last time volume logged, cleared on endInterval
211         mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
212         mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
213         mMinVolumeDurationNs = 0;
214         mMaxVolumeDurationNs = 0;
215 
216         mDeviceLatencyMs.reset();
217         mDeviceStartupMs.reset();
218 
219         mUnderrunCountSinceIntervalGroup = mUnderrunCount;
220         mUnderrunFramesSinceIntervalGroup = mUnderrunFrames;
221         // do not reset mUnderrunCount - it keeps continuously running for tracks.
222     }
223 
224     const std::string mMetricsId;
225     const bool        mIsOut;  // if true, than a playback track, otherwise used for record.
226 
227     mutable           std::mutex mLock;
228 
229     // Devices in the interval group.
230     std::string       mDevices GUARDED_BY(mLock);
231 
232     // Number of intervals and playing time
233     int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
234     int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
235     int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;     // time on device.
236 
237     // Average volume
238     double            mVolume GUARDED_BY(mLock) = 0.f;       // last set volume.
239     double            mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
240     int64_t           mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
241 
242     // Min/Max volume
243     double            mMinVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
244     double            mMaxVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
245     int64_t           mMinVolumeDurationNs GUARDED_BY(mLock) = 0;
246     int64_t           mMaxVolumeDurationNs GUARDED_BY(mLock) = 0;
247 
248     // latency and startup for each interval.
249     audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
250     audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock);
251 
252     // underrun count and frames
253     int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;
254     int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;
255     int64_t           mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0;
256     int64_t           mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0;
257 };
258 
259 } // namespace android
260 
261 #endif // ANDROID_AUDIO_TRACKMETRICS_H
262