• 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 #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