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 <binder/IActivityManager.h> 21 #include <binder/IPCThreadState.h> 22 #include <binder/IServiceManager.h> 23 #include <mutex> 24 25 namespace android { 26 27 /** 28 * TrackMetrics handles the AudioFlinger track metrics. 29 * 30 * We aggregate metrics for a particular device for proper analysis. 31 * This includes power, performance, and usage metrics. 32 * 33 * This class is thread-safe with a lock for safety. There is no risk of deadlock 34 * as this class only executes external one-way calls in Mediametrics and does not 35 * call any other AudioFlinger class. 36 * 37 * Terminology: 38 * An AudioInterval is a contiguous playback segment. 39 * An AudioIntervalGroup is a group of continuous playback segments on the same device. 40 * 41 * We currently deliver metrics based on an AudioIntervalGroup. 42 */ 43 class TrackMetrics final { 44 45 46 public: TrackMetrics(std::string metricsId,bool isOut,int clientUid)47 TrackMetrics(std::string metricsId, bool isOut, int clientUid) 48 : mMetricsId(std::move(metricsId)) 49 , mIsOut(isOut) 50 , mUid(clientUid) 51 {} // we don't log a constructor item, we wait for more info in logConstructor(). 52 ~TrackMetrics()53 ~TrackMetrics() { 54 logEndInterval(); 55 std::lock_guard l(mLock); 56 deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP); 57 // we don't log a destructor item here. 58 } 59 60 // Called under the following circumstances 61 // 1) when we are added to the Thread 62 // 2) when we have a createPatch in the Thread. logBeginInterval(const std::string & devices)63 void logBeginInterval(const std::string& devices) { 64 std::lock_guard l(mLock); 65 if (mDevices != devices) { 66 deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP); 67 mDevices = devices; 68 resetIntervalGroupMetrics(); 69 deliverDeviceMetrics( 70 AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str()); 71 } 72 ++mIntervalCount; 73 const auto& mActivityManager = getActivityManager(); 74 if (mActivityManager) { 75 if (mIsOut) { 76 mActivityManager->logFgsApiBegin(AUDIO_API, 77 mUid, 78 IPCThreadState::self() -> getCallingPid()); 79 } else { 80 mActivityManager->logFgsApiBegin(MICROPHONE_API, 81 mUid, 82 IPCThreadState::self() -> getCallingPid()); 83 } 84 } 85 } 86 87 void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId, 88 const std::string& traits = {}, 89 audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const { 90 // Once this item is logged by the server, the client can add properties. 91 // no lock required, all local or const variables. 92 mediametrics::LogItem item(mMetricsId); 93 item.setPid(creatorPid) 94 .setUid(creatorUid) 95 .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid) 96 .set(AMEDIAMETRICS_PROP_EVENT, 97 AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR) 98 .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId) 99 .set(AMEDIAMETRICS_PROP_TRAITS, traits); 100 // log streamType from the service, since client doesn't know chosen streamType. 101 if (streamType != AUDIO_STREAM_DEFAULT) { 102 item.set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(streamType).c_str()); 103 } 104 item.record(); 105 } 106 107 // Called when we are removed from the Thread. logEndInterval()108 void logEndInterval() { 109 std::lock_guard l(mLock); 110 if (mLastVolumeChangeTimeNs != 0) { 111 logVolume_l(mVolume); // flush out the last volume. 112 mLastVolumeChangeTimeNs = 0; 113 } 114 const auto& mActivityManager = getActivityManager(); 115 if (mActivityManager) { 116 if (mIsOut) { 117 mActivityManager->logFgsApiEnd(AUDIO_API, 118 mUid, 119 IPCThreadState::self() -> getCallingPid()); 120 } else { 121 mActivityManager->logFgsApiEnd(MICROPHONE_API, 122 mUid, 123 IPCThreadState::self() -> getCallingPid()); 124 } 125 } 126 } 127 logInvalidate()128 void logInvalidate() const { 129 // no lock required, all local or const variables. 130 mediametrics::LogItem(mMetricsId) 131 .set(AMEDIAMETRICS_PROP_EVENT, 132 AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE) 133 .record(); 134 } 135 logLatencyAndStartup(double latencyMs,double startupMs)136 void logLatencyAndStartup(double latencyMs, double startupMs) { 137 mediametrics::LogItem(mMetricsId) 138 .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs) 139 .set(AMEDIAMETRICS_PROP_STARTUPMS, startupMs) 140 .record(); 141 std::lock_guard l(mLock); 142 mDeviceLatencyMs.add(latencyMs); 143 mDeviceStartupMs.add(startupMs); 144 } 145 updateMinMaxVolume_l(int64_t durationNs,double deviceVolume)146 void updateMinMaxVolume_l(int64_t durationNs, double deviceVolume) 147 REQUIRES(mLock) { 148 if (deviceVolume > mMaxVolume) { 149 mMaxVolume = deviceVolume; 150 mMaxVolumeDurationNs = durationNs; 151 } else if (deviceVolume == mMaxVolume) { 152 mMaxVolumeDurationNs += durationNs; 153 } 154 if (deviceVolume < mMinVolume) { 155 mMinVolume = deviceVolume; 156 mMinVolumeDurationNs = durationNs; 157 } else if (deviceVolume == mMinVolume) { 158 mMinVolumeDurationNs += durationNs; 159 } 160 } 161 162 // may be called multiple times during an interval logVolume(float volume)163 void logVolume(float volume) { 164 std::lock_guard l(mLock); 165 logVolume_l(volume); 166 } 167 168 // Use absolute numbers returned by AudioTrackShared. logUnderruns(size_t count,size_t frames)169 void logUnderruns(size_t count, size_t frames) { 170 std::lock_guard l(mLock); 171 mUnderrunCount = count; 172 mUnderrunFrames = frames; 173 // Consider delivering a message here (also be aware of excessive spam). 174 } 175 176 private: 177 178 // no lock required - all arguments and constants. deliverDeviceMetrics(const char * eventName,const char * devices)179 void deliverDeviceMetrics(const char *eventName, const char *devices) const { 180 mediametrics::LogItem(mMetricsId) 181 .set(AMEDIAMETRICS_PROP_EVENT, eventName) 182 .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES 183 : AMEDIAMETRICS_PROP_INPUTDEVICES, devices) 184 .record(); 185 } 186 logVolume_l(float volume)187 void logVolume_l(float volume) REQUIRES(mLock) { 188 const int64_t timeNs = systemTime(); 189 const int64_t durationNs = mLastVolumeChangeTimeNs == 0 190 ? 0 : timeNs - mLastVolumeChangeTimeNs; 191 if (durationNs > 0) { 192 // See West's algorithm for weighted averages 193 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance 194 mDeviceVolume += (mVolume - mDeviceVolume) * durationNs 195 / (durationNs + mDeviceTimeNs); 196 mDeviceTimeNs += durationNs; 197 mCumulativeTimeNs += durationNs; 198 } 199 updateMinMaxVolume_l(durationNs, mVolume); // always update. 200 mVolume = volume; 201 mLastVolumeChangeTimeNs = timeNs; 202 } 203 deliverCumulativeMetrics(const char * eventName)204 void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) { 205 if (mIntervalCount > 0) { 206 mediametrics::LogItem item(mMetricsId); 207 item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs) 208 .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs) 209 .set(AMEDIAMETRICS_PROP_EVENT, eventName) 210 .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount); 211 if (mIsOut) { 212 item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume) 213 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUMEDURATIONNS, mMaxVolumeDurationNs) 214 .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUME, mMaxVolume) 215 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUMEDURATIONNS, mMinVolumeDurationNs) 216 .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUME, mMinVolume); 217 } 218 if (mDeviceLatencyMs.getN() > 0) { 219 item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean()) 220 .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean()); 221 } 222 if (mUnderrunCount > 0) { 223 item.set(AMEDIAMETRICS_PROP_UNDERRUN, 224 (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup)) 225 .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, 226 (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup)); 227 } 228 item.record(); 229 } 230 } 231 resetIntervalGroupMetrics()232 void resetIntervalGroupMetrics() REQUIRES(mLock) { 233 // mDevices is not reset by resetIntervalGroupMetrics. 234 235 mIntervalCount = 0; 236 // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics. 237 mDeviceTimeNs = 0; 238 239 mVolume = 0.f; 240 mDeviceVolume = 0.f; 241 mLastVolumeChangeTimeNs = 0; // last time volume logged, cleared on endInterval 242 mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME; 243 mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME; 244 mMinVolumeDurationNs = 0; 245 mMaxVolumeDurationNs = 0; 246 247 mDeviceLatencyMs.reset(); 248 mDeviceStartupMs.reset(); 249 250 mUnderrunCountSinceIntervalGroup = mUnderrunCount; 251 mUnderrunFramesSinceIntervalGroup = mUnderrunFrames; 252 // do not reset mUnderrunCount - it keeps continuously running for tracks. 253 } 254 255 // Meyer's singleton is thread-safe. getActivityManager()256 static const sp<IActivityManager>& getActivityManager() { 257 static const auto activityManager = []() -> sp<IActivityManager> { 258 const sp<IServiceManager> sm(defaultServiceManager()); 259 if (sm != nullptr) { 260 return interface_cast<IActivityManager>(sm->checkService(String16("activity"))); 261 } 262 return nullptr; 263 }(); 264 return activityManager; 265 } 266 267 const std::string mMetricsId; 268 const bool mIsOut; // if true, than a playback track, otherwise used for record. 269 270 static constexpr int AUDIO_API = 5; 271 static constexpr int MICROPHONE_API = 6; 272 const int mUid; 273 274 mutable std::mutex mLock; 275 276 // Devices in the interval group. 277 std::string mDevices GUARDED_BY(mLock); 278 279 // Number of intervals and playing time 280 int32_t mIntervalCount GUARDED_BY(mLock) = 0; 281 int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time. 282 int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0; // time on device. 283 284 // Average volume 285 double mVolume GUARDED_BY(mLock) = 0.f; // last set volume. 286 double mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume. 287 int64_t mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0; 288 289 // Min/Max volume 290 double mMinVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MIN_VOLUME; 291 double mMaxVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MAX_VOLUME; 292 int64_t mMinVolumeDurationNs GUARDED_BY(mLock) = 0; 293 int64_t mMaxVolumeDurationNs GUARDED_BY(mLock) = 0; 294 295 // latency and startup for each interval. 296 audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock); 297 audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock); 298 299 // underrun count and frames 300 int64_t mUnderrunCount GUARDED_BY(mLock) = 0; 301 int64_t mUnderrunFrames GUARDED_BY(mLock) = 0; 302 int64_t mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0; 303 int64_t mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0; 304 }; 305 306 } // namespace android 307 308 #endif // ANDROID_AUDIO_TRACKMETRICS_H 309