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