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         mIntervalStartTimeNs = systemTime();
68     }
69 
70     void logConstructor(pid_t creatorPid, uid_t creatorUid,
71             const std::string& traits = {},
72             audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
73         // Once this item is logged by the server, the client can add properties.
74         // no lock required, all local or const variables.
75         mediametrics::LogItem item(mMetricsId);
76         item.setPid(creatorPid)
77             .setUid(creatorUid)
78             .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
79             .set(AMEDIAMETRICS_PROP_EVENT,
80                     AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
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 (mIntervalStartTimeNs != 0) {
93             const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
94             mIntervalStartTimeNs = 0;
95             mCumulativeTimeNs += elapsedTimeNs;
96             mDeviceTimeNs += elapsedTimeNs;
97         }
98     }
99 
logInvalidate()100     void logInvalidate() const {
101         // no lock required, all local or const variables.
102         mediametrics::LogItem(mMetricsId)
103             .set(AMEDIAMETRICS_PROP_EVENT,
104                  AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE)
105             .record();
106     }
107 
logLatencyAndStartup(double latencyMs,double startupMs)108     void logLatencyAndStartup(double latencyMs, double startupMs) {
109         mediametrics::LogItem(mMetricsId)
110             .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
111             .set(AMEDIAMETRICS_PROP_STARTUPMS, startupMs)
112             .record();
113         std::lock_guard l(mLock);
114         mDeviceLatencyMs.add(latencyMs);
115         mDeviceStartupMs.add(startupMs);
116     }
117 
118     // may be called multiple times during an interval
logVolume(float volume)119     void logVolume(float volume) {
120         const int64_t timeNs = systemTime();
121         std::lock_guard l(mLock);
122         if (mStartVolumeTimeNs == 0) {
123             mDeviceVolume = mVolume = volume;
124             mLastVolumeChangeTimeNs = mStartVolumeTimeNs = timeNs;
125             return;
126         }
127         mDeviceVolume = (mDeviceVolume * (mLastVolumeChangeTimeNs - mStartVolumeTimeNs) +
128             mVolume * (timeNs - mLastVolumeChangeTimeNs)) / (timeNs - mStartVolumeTimeNs);
129         mVolume = volume;
130         mLastVolumeChangeTimeNs = timeNs;
131     }
132 
133     // Use absolute numbers returned by AudioTrackShared.
logUnderruns(size_t count,size_t frames)134     void logUnderruns(size_t count, size_t frames) {
135         std::lock_guard l(mLock);
136         mUnderrunCount = count;
137         mUnderrunFrames = frames;
138         // Consider delivering a message here (also be aware of excessive spam).
139     }
140 
141 private:
142     // no lock required - all arguments and constants.
deliverDeviceMetrics(const char * eventName,const char * devices)143     void deliverDeviceMetrics(const char *eventName, const char *devices) const {
144         mediametrics::LogItem(mMetricsId)
145             .set(AMEDIAMETRICS_PROP_EVENT, eventName)
146             .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
147                    : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
148            .record();
149     }
150 
deliverCumulativeMetrics(const char * eventName)151     void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
152         if (mIntervalCount > 0) {
153             mediametrics::LogItem item(mMetricsId);
154             item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
155                 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
156                 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
157                 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
158             if (mIsOut) {
159                 item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume);
160             }
161             if (mDeviceLatencyMs.getN() > 0) {
162                 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean())
163                     .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean());
164             }
165             if (mUnderrunCount > 0) {
166                 item.set(AMEDIAMETRICS_PROP_UNDERRUN,
167                         (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup))
168                     .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES,
169                         (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup));
170             }
171             item.record();
172         }
173     }
174 
resetIntervalGroupMetrics()175     void resetIntervalGroupMetrics() REQUIRES(mLock) {
176         // mDevices is not reset by resetIntervalGroupMetrics.
177 
178         mIntervalCount = 0;
179         mIntervalStartTimeNs = 0;
180         // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
181         mDeviceTimeNs = 0;
182 
183         mVolume = 0.f;
184         mDeviceVolume = 0.f;
185         mStartVolumeTimeNs = 0;
186         mLastVolumeChangeTimeNs = 0;
187 
188         mDeviceLatencyMs.reset();
189         mDeviceStartupMs.reset();
190 
191         mUnderrunCountSinceIntervalGroup = mUnderrunCount;
192         mUnderrunFramesSinceIntervalGroup = mUnderrunFrames;
193         // do not reset mUnderrunCount - it keeps continuously running for tracks.
194     }
195 
196     const std::string mMetricsId;
197     const bool        mIsOut;  // if true, than a playback track, otherwise used for record.
198 
199     mutable           std::mutex mLock;
200 
201     // Devices in the interval group.
202     std::string       mDevices GUARDED_BY(mLock);
203 
204     // Number of intervals and playing time
205     int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
206     int64_t           mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
207     int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0;
208     int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;
209 
210     // Average volume
211     double            mVolume GUARDED_BY(mLock) = 0.f;
212     double            mDeviceVolume GUARDED_BY(mLock) = 0.f;
213     int64_t           mStartVolumeTimeNs GUARDED_BY(mLock) = 0;
214     int64_t           mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
215 
216     // latency and startup for each interval.
217     audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
218     audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock);
219 
220     // underrun count and frames
221     int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;
222     int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;
223     int64_t           mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0;
224     int64_t           mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0;
225 };
226 
227 } // namespace android
228 
229 #endif // ANDROID_AUDIO_TRACKMETRICS_H
230