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, int32_t internalTrackId, 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_INTERNALTRACKID, internalTrackId) 82 .set(AMEDIAMETRICS_PROP_TRAITS, traits); 83 // 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(); 88 } 89 90 // Called when we are removed from the Thread. logEndInterval()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 logInvalidate()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 logLatencyAndStartup(double latencyMs,double startupMs)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 logVolume(float volume)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. logUnderruns(size_t count,size_t frames)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 142 private: 143 // no lock required - all arguments and constants. deliverDeviceMetrics(const char * eventName,const char * devices)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 deliverCumulativeMetrics(const char * eventName)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 resetIntervalGroupMetrics()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 231