• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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