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