blob: ed3928a6041413e8cb9492dbaf335d2bccaf1e8c [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;
Andy Hungc2b11cb2020-04-22 09:04:01 -070067 }
68
Andy Hung5837c7f2021-02-25 10:48:24 -080069 void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
Andy Hunga629bd12020-06-05 16:03:53 -070070 const std::string& traits = {},
Andy Hungea840382020-05-05 21:50:17 -070071 audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
Andy Hungc2b11cb2020-04-22 09:04:01 -070072 // Once this item is logged by the server, the client can add properties.
73 // no lock required, all local or const variables.
Andy Hungea840382020-05-05 21:50:17 -070074 mediametrics::LogItem item(mMetricsId);
75 item.setPid(creatorPid)
Andy Hungc2b11cb2020-04-22 09:04:01 -070076 .setUid(creatorUid)
77 .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
78 .set(AMEDIAMETRICS_PROP_EVENT,
Andy Hunga629bd12020-06-05 16:03:53 -070079 AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
Andy Hung5837c7f2021-02-25 10:48:24 -080080 .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId)
Andy Hunga629bd12020-06-05 16:03:53 -070081 .set(AMEDIAMETRICS_PROP_TRAITS, traits);
Andy Hungea840382020-05-05 21:50:17 -070082 // 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();
Andy Hungc2b11cb2020-04-22 09:04:01 -070087 }
88
89 // Called when we are removed from the Thread.
90 void logEndInterval() {
91 std::lock_guard l(mLock);
Andy Hungcb6cc752022-05-19 19:24:51 -070092 if (mLastVolumeChangeTimeNs != 0) {
93 logVolume_l(mVolume); // flush out the last volume.
94 mLastVolumeChangeTimeNs = 0;
Andy Hungc2b11cb2020-04-22 09:04:01 -070095 }
96 }
97
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
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
Andy Hung71ba4b32022-10-06 12:09:49 -0700116 void updateMinMaxVolume_l(int64_t durationNs, double deviceVolume)
117 REQUIRES(mLock) {
Robert Lee36ac0412022-01-13 03:05:41 +0000118 if (deviceVolume > mMaxVolume) {
119 mMaxVolume = deviceVolume;
120 mMaxVolumeDurationNs = durationNs;
121 } else if (deviceVolume == mMaxVolume) {
122 mMaxVolumeDurationNs += durationNs;
123 }
124 if (deviceVolume < mMinVolume) {
125 mMinVolume = deviceVolume;
126 mMinVolumeDurationNs = durationNs;
127 } else if (deviceVolume == mMinVolume) {
128 mMinVolumeDurationNs += durationNs;
129 }
130 }
131
Andy Hungc2b11cb2020-04-22 09:04:01 -0700132 // may be called multiple times during an interval
133 void logVolume(float volume) {
Andy Hungc2b11cb2020-04-22 09:04:01 -0700134 std::lock_guard l(mLock);
Andy Hungcb6cc752022-05-19 19:24:51 -0700135 logVolume_l(volume);
Andy Hungc2b11cb2020-04-22 09:04:01 -0700136 }
137
138 // Use absolute numbers returned by AudioTrackShared.
139 void logUnderruns(size_t count, size_t frames) {
140 std::lock_guard l(mLock);
141 mUnderrunCount = count;
142 mUnderrunFrames = frames;
143 // Consider delivering a message here (also be aware of excessive spam).
144 }
145
146private:
Andy Hungcb6cc752022-05-19 19:24:51 -0700147
Andy Hungc2b11cb2020-04-22 09:04:01 -0700148 // no lock required - all arguments and constants.
149 void deliverDeviceMetrics(const char *eventName, const char *devices) const {
150 mediametrics::LogItem(mMetricsId)
151 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
152 .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
153 : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
154 .record();
155 }
156
Andy Hungcb6cc752022-05-19 19:24:51 -0700157 void logVolume_l(float volume) REQUIRES(mLock) {
158 const int64_t timeNs = systemTime();
159 const int64_t durationNs = mLastVolumeChangeTimeNs == 0
160 ? 0 : timeNs - mLastVolumeChangeTimeNs;
161 if (durationNs > 0) {
162 // See West's algorithm for weighted averages
163 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
164 mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
165 / (durationNs + mDeviceTimeNs);
166 mDeviceTimeNs += durationNs;
167 mCumulativeTimeNs += durationNs;
168 }
Andy Hung71ba4b32022-10-06 12:09:49 -0700169 updateMinMaxVolume_l(durationNs, mVolume); // always update.
Andy Hungcb6cc752022-05-19 19:24:51 -0700170 mVolume = volume;
171 mLastVolumeChangeTimeNs = timeNs;
172 }
173
Andy Hungc2b11cb2020-04-22 09:04:01 -0700174 void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
175 if (mIntervalCount > 0) {
176 mediametrics::LogItem item(mMetricsId);
177 item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
178 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
179 .set(AMEDIAMETRICS_PROP_EVENT, eventName)
180 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
181 if (mIsOut) {
Robert Lee36ac0412022-01-13 03:05:41 +0000182 item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume)
183 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUMEDURATIONNS, mMaxVolumeDurationNs)
184 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUME, mMaxVolume)
185 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUMEDURATIONNS, mMinVolumeDurationNs)
186 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUME, mMinVolume);
Andy Hungc2b11cb2020-04-22 09:04:01 -0700187 }
188 if (mDeviceLatencyMs.getN() > 0) {
189 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean())
190 .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean());
191 }
192 if (mUnderrunCount > 0) {
193 item.set(AMEDIAMETRICS_PROP_UNDERRUN,
194 (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup))
195 .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES,
196 (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup));
197 }
198 item.record();
199 }
200 }
201
202 void resetIntervalGroupMetrics() REQUIRES(mLock) {
203 // mDevices is not reset by resetIntervalGroupMetrics.
204
205 mIntervalCount = 0;
Andy Hungc2b11cb2020-04-22 09:04:01 -0700206 // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
207 mDeviceTimeNs = 0;
208
209 mVolume = 0.f;
210 mDeviceVolume = 0.f;
Andy Hungcb6cc752022-05-19 19:24:51 -0700211 mLastVolumeChangeTimeNs = 0; // last time volume logged, cleared on endInterval
Robert Lee36ac0412022-01-13 03:05:41 +0000212 mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
213 mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
214 mMinVolumeDurationNs = 0;
215 mMaxVolumeDurationNs = 0;
Andy Hungc2b11cb2020-04-22 09:04:01 -0700216
217 mDeviceLatencyMs.reset();
218 mDeviceStartupMs.reset();
219
220 mUnderrunCountSinceIntervalGroup = mUnderrunCount;
221 mUnderrunFramesSinceIntervalGroup = mUnderrunFrames;
222 // do not reset mUnderrunCount - it keeps continuously running for tracks.
223 }
224
225 const std::string mMetricsId;
226 const bool mIsOut; // if true, than a playback track, otherwise used for record.
227
228 mutable std::mutex mLock;
229
230 // Devices in the interval group.
231 std::string mDevices GUARDED_BY(mLock);
232
233 // Number of intervals and playing time
234 int32_t mIntervalCount GUARDED_BY(mLock) = 0;
Andy Hungcb6cc752022-05-19 19:24:51 -0700235 int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
236 int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0; // time on device.
Andy Hungc2b11cb2020-04-22 09:04:01 -0700237
238 // Average volume
Andy Hungcb6cc752022-05-19 19:24:51 -0700239 double mVolume GUARDED_BY(mLock) = 0.f; // last set volume.
240 double mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
Andy Hungc2b11cb2020-04-22 09:04:01 -0700241 int64_t mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
242
Robert Lee36ac0412022-01-13 03:05:41 +0000243 // Min/Max volume
244 double mMinVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
245 double mMaxVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
246 int64_t mMinVolumeDurationNs GUARDED_BY(mLock) = 0;
247 int64_t mMaxVolumeDurationNs GUARDED_BY(mLock) = 0;
248
Andy Hungc2b11cb2020-04-22 09:04:01 -0700249 // latency and startup for each interval.
250 audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
251 audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock);
252
253 // underrun count and frames
254 int64_t mUnderrunCount GUARDED_BY(mLock) = 0;
255 int64_t mUnderrunFrames GUARDED_BY(mLock) = 0;
256 int64_t mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0;
257 int64_t mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0;
258};
259
260} // namespace android
261
262#endif // ANDROID_AUDIO_TRACKMETRICS_H