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