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