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 } 68 69 void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId, 70 const std::string& traits = {}, 71 audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const { 72 // Once this item is logged by the server, the client can add properties. 73 // no lock required, all local or const variables. 74 mediametrics::LogItem item(mMetricsId); 75 item.setPid(creatorPid) 76 .setUid(creatorUid) 77 .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid) 78 .set(AMEDIAMETRICS_PROP_EVENT, 79 AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR) 80 .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId) 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 (mLastVolumeChangeTimeNs != 0) { 93 logVolume_l(mVolume); // flush out the last volume. 94 mLastVolumeChangeTimeNs = 0; 95 } 96 } 97 logInvalidate()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 logLatencyAndStartup(double latencyMs,double startupMs)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 updateMinMaxVolume(int64_t durationNs,double deviceVolume)116 void updateMinMaxVolume(int64_t durationNs, double deviceVolume) { 117 if (deviceVolume > mMaxVolume) { 118 mMaxVolume = deviceVolume; 119 mMaxVolumeDurationNs = durationNs; 120 } else if (deviceVolume == mMaxVolume) { 121 mMaxVolumeDurationNs += durationNs; 122 } 123 if (deviceVolume < mMinVolume) { 124 mMinVolume = deviceVolume; 125 mMinVolumeDurationNs = durationNs; 126 } else if (deviceVolume == mMinVolume) { 127 mMinVolumeDurationNs += durationNs; 128 } 129 } 130 131 // may be called multiple times during an interval logVolume(float volume)132 void logVolume(float volume) { 133 std::lock_guard l(mLock); 134 logVolume_l(volume); 135 } 136 137 // Use absolute numbers returned by AudioTrackShared. logUnderruns(size_t count,size_t frames)138 void logUnderruns(size_t count, size_t frames) { 139 std::lock_guard l(mLock); 140 mUnderrunCount = count; 141 mUnderrunFrames = frames; 142 // Consider delivering a message here (also be aware of excessive spam). 143 } 144 145 private: 146 147 // no lock required - all arguments and constants. deliverDeviceMetrics(const char * eventName,const char * devices)148 void deliverDeviceMetrics(const char *eventName, const char *devices) const { 149 mediametrics::LogItem(mMetricsId) 150 .set(AMEDIAMETRICS_PROP_EVENT, eventName) 151 .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES 152 : AMEDIAMETRICS_PROP_INPUTDEVICES, devices) 153 .record(); 154 } 155 logVolume_l(float volume)156 void logVolume_l(float volume) REQUIRES(mLock) { 157 const int64_t timeNs = systemTime(); 158 const int64_t durationNs = mLastVolumeChangeTimeNs == 0 159 ? 0 : timeNs - mLastVolumeChangeTimeNs; 160 if (durationNs > 0) { 161 // See West's algorithm for weighted averages 162 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance 163 mDeviceVolume += (mVolume - mDeviceVolume) * durationNs 164 / (durationNs + mDeviceTimeNs); 165 mDeviceTimeNs += durationNs; 166 mCumulativeTimeNs += durationNs; 167 } 168 updateMinMaxVolume(durationNs, mVolume); // always update. 169 mVolume = volume; 170 mLastVolumeChangeTimeNs = timeNs; 171 } 172 deliverCumulativeMetrics(const char * eventName)173 void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) { 174 if (mIntervalCount > 0) { 175 mediametrics::LogItem item(mMetricsId); 176 item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs) 177 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs) 178 .set(AMEDIAMETRICS_PROP_EVENT, eventName) 179 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount); 180 if (mIsOut) { 181 item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume) 182 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUMEDURATIONNS, mMaxVolumeDurationNs) 183 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUME, mMaxVolume) 184 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUMEDURATIONNS, mMinVolumeDurationNs) 185 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUME, mMinVolume); 186 } 187 if (mDeviceLatencyMs.getN() > 0) { 188 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean()) 189 .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean()); 190 } 191 if (mUnderrunCount > 0) { 192 item.set(AMEDIAMETRICS_PROP_UNDERRUN, 193 (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup)) 194 .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, 195 (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup)); 196 } 197 item.record(); 198 } 199 } 200 resetIntervalGroupMetrics()201 void resetIntervalGroupMetrics() REQUIRES(mLock) { 202 // mDevices is not reset by resetIntervalGroupMetrics. 203 204 mIntervalCount = 0; 205 // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics. 206 mDeviceTimeNs = 0; 207 208 mVolume = 0.f; 209 mDeviceVolume = 0.f; 210 mLastVolumeChangeTimeNs = 0; // last time volume logged, cleared on endInterval 211 mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME; 212 mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME; 213 mMinVolumeDurationNs = 0; 214 mMaxVolumeDurationNs = 0; 215 216 mDeviceLatencyMs.reset(); 217 mDeviceStartupMs.reset(); 218 219 mUnderrunCountSinceIntervalGroup = mUnderrunCount; 220 mUnderrunFramesSinceIntervalGroup = mUnderrunFrames; 221 // do not reset mUnderrunCount - it keeps continuously running for tracks. 222 } 223 224 const std::string mMetricsId; 225 const bool mIsOut; // if true, than a playback track, otherwise used for record. 226 227 mutable std::mutex mLock; 228 229 // Devices in the interval group. 230 std::string mDevices GUARDED_BY(mLock); 231 232 // Number of intervals and playing time 233 int32_t mIntervalCount GUARDED_BY(mLock) = 0; 234 int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time. 235 int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0; // time on device. 236 237 // Average volume 238 double mVolume GUARDED_BY(mLock) = 0.f; // last set volume. 239 double mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume. 240 int64_t mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0; 241 242 // Min/Max volume 243 double mMinVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MIN_VOLUME; 244 double mMaxVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MAX_VOLUME; 245 int64_t mMinVolumeDurationNs GUARDED_BY(mLock) = 0; 246 int64_t mMaxVolumeDurationNs GUARDED_BY(mLock) = 0; 247 248 // latency and startup for each interval. 249 audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock); 250 audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock); 251 252 // underrun count and frames 253 int64_t mUnderrunCount GUARDED_BY(mLock) = 0; 254 int64_t mUnderrunFrames GUARDED_BY(mLock) = 0; 255 int64_t mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0; 256 int64_t mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0; 257 }; 258 259 } // namespace android 260 261 #endif // ANDROID_AUDIO_TRACKMETRICS_H 262