• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 <android-base/thread_annotations.h>
20 #include "AnalyticsActions.h"
21 #include "AnalyticsState.h"
22 #include "AudioPowerUsage.h"
23 #include "HeatMap.h"
24 #include "StatsdLog.h"
25 #include "TimedAction.h"
26 #include "Wrap.h"
27 
28 namespace android::mediametrics {
29 
30 class AudioAnalytics
31 {
32     // AudioAnalytics action / state helper classes
33     friend AudioPowerUsage;
34 
35 public:
36     explicit AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog);
37     ~AudioAnalytics();
38 
39     /**
40      * Returns success if AudioAnalytics recognizes item.
41      *
42      * AudioAnalytics requires the item key to start with "audio.".
43      *
44      * A trusted source can create a new key, an untrusted source
45      * can only modify the key if the uid will match that authorized
46      * on the existing key.
47      *
48      * \param item the item to be submitted.
49      * \param isTrusted whether the transaction comes from a trusted source.
50      *        In this case, a trusted source is verified by binder
51      *        UID to be a system service by MediaMetrics service.
52      *        Do not use true if you haven't really checked!
53      *
54      * \return NO_ERROR on success,
55      *         PERMISSION_DENIED if the item cannot be put into the AnalyticsState,
56      *         BAD_VALUE if the item key does not start with "audio.".
57      */
58     status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted);
59 
60     /**
61      * Returns a pair consisting of the dump string, and the number of lines in the string.
62      *
63      * The number of lines in the returned pair is used as an optimization
64      * for subsequent line limiting.
65      *
66      * The TimeMachine and the TransactionLog are dumped separately under
67      * different locks, so may not be 100% consistent with the last data
68      * delivered.
69      *
70      * \param lines the maximum number of lines in the string returned.
71      * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
72      * \param prefix the desired key prefix to match (nullptr shows all)
73      */
74     std::pair<std::string, int32_t> dump(
75             int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const;
76 
77     /**
78      * Returns a pair consisting of the dump string and the number of lines in the string.
79      *
80      * HeatMap dump.
81      */
82     std::pair<std::string, int32_t> dumpHeatMap(int32_t lines = INT32_MAX) const {
83         return mHeatMap.dump(lines);
84     }
85 
86     /**
87      * Returns a pair consisting of the dump string and the number of lines in the string.
88      *
89      * Health dump.
90      */
91     std::pair<std::string, int32_t> dumpHealth(int32_t lines = INT32_MAX) const {
92         return mHealth.dump(lines);
93     }
94 
95     /**
96      * Returns a pair consisting of the dump string and the number of lines in the string.
97      *
98      * Spatializer dump.
99      */
100     std::pair<std::string, int32_t> dumpSpatializer(int32_t lines = INT32_MAX) const {
101         return mSpatializer.dump(lines);
102     }
103 
clear()104     void clear() {
105         // underlying state is locked.
106         mPreviousAnalyticsState->clear();
107         mAnalyticsState->clear();
108 
109         // Clears the status map
110         mHeatMap.clear();
111 
112         // Clear power usage state.
113         mAudioPowerUsage.clear();
114     }
115 
116 private:
117 
118     /*
119      * AudioAnalytics class does not contain a monitor mutex.
120      * Instead, all of its variables are individually locked for access.
121      * Since data and items are generally added only (gc removes it), this is a reasonable
122      * compromise for availability/concurrency versus consistency.
123      *
124      * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics.
125      * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be
126      * used to achieve better consistency if needed.
127      */
128 
129     /**
130      * Processes any pending actions for a particular item.
131      *
132      * \param item to check against the current AnalyticsActions.
133      */
134     void processActions(const std::shared_ptr<const mediametrics::Item>& item);
135 
136     /**
137      * Processes status information contained in the item.
138      *
139      * \param item to check against for status handling
140      */
141     void processStatus(const std::shared_ptr<const mediametrics::Item>& item);
142 
143     // Specific reporting methods
144     bool reportAudioRecordStatus(
145             const std::shared_ptr<const mediametrics::Item>& item,
146             const std::string& key, const std::string& eventStr,
147             const std::string& statusString, uid_t uid, const std::string& message,
148             int32_t subCode) const;
149 
150     bool reportAudioTrackStatus(
151             const std::shared_ptr<const mediametrics::Item>& item,
152             const std::string& key, const std::string& eventStr,
153             const std::string& statusString, uid_t uid, const std::string& message,
154             int32_t subCode) const;
155 
156     // HELPER METHODS
157     /**
158      * Return the audio thread associated with an audio track name.
159      * e.g. "audio.track.32" -> "audio.thread.10" if the associated
160      * threadId for the audio track is 10.
161      */
162     std::string getThreadFromTrack(const std::string& track) const;
163 
164     /**
165      * return the device name, if present.
166      *
167      * This is currently enabled only for Bluetooth output devices.
168      */
169     std::string getDeviceNamesFromOutputDevices(std::string_view devices) const;
170 
171     const bool mDeliverStatistics;
172 
173     // Actions is individually locked
174     AnalyticsActions mActions;
175 
176     // AnalyticsState is individually locked, and we use SharedPtrWrap
177     // to allow safe access even if the shared pointer changes underneath.
178     // These wrap pointers always point to a valid state object.
179     SharedPtrWrap<AnalyticsState> mAnalyticsState;
180     SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
181 
182     TimedAction mTimedAction; // locked internally
183     const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads.
184 
185     static constexpr size_t kHeatEntries = 100;
186     HeatMap mHeatMap{kHeatEntries}; // locked internally, ok for multiple threads.
187 
188     // DeviceUse is a nested class which handles audio device usage accounting.
189     // We define this class at the end to ensure prior variables all properly constructed.
190     // TODO: Track / Thread interaction
191     // TODO: Consider statistics aggregation.
192     class DeviceUse {
193     public:
194         enum ItemType {
195             RECORD = 0,
196             THREAD = 1,
197             TRACK = 2,
198         };
199 
DeviceUse(AudioAnalytics & audioAnalytics)200         explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
201 
202         // Called every time an endAudioIntervalGroup message is received.
203         void endAudioIntervalGroup(
204                 const std::shared_ptr<const android::mediametrics::Item> &item,
205                 ItemType itemType) const;
206 
207     private:
208         AudioAnalytics &mAudioAnalytics;
209     } mDeviceUse{*this};
210 
211     // DeviceConnected is a nested class which handles audio device connection
212     // We define this class at the end to ensure prior variables all properly constructed.
213     // TODO: Track / Thread interaction
214     // TODO: Consider statistics aggregation.
215     class DeviceConnection {
216     public:
DeviceConnection(AudioAnalytics & audioAnalytics)217         explicit DeviceConnection(AudioAnalytics &audioAnalytics)
218             : mAudioAnalytics{audioAnalytics} {}
219 
220         // Called every time an endAudioIntervalGroup message is received.
221         void a2dpConnected(
222                 const std::shared_ptr<const android::mediametrics::Item> &item);
223 
224         // Called when we have an AudioFlinger createPatch
225         void createPatch(
226                 const std::shared_ptr<const android::mediametrics::Item> &item);
227 
228         // Called through AudioManager when the BT service wants to notify connection
229         void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
230                 const std::shared_ptr<const android::mediametrics::Item> &item);
231 
232         // When the timer expires.
233         void expire();
234 
235     private:
236         AudioAnalytics &mAudioAnalytics;
237 
238         mutable std::mutex mLock;
239         std::string mA2dpDeviceName;
240         int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0;  // Time for BT service request.
241         int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0;  // Time audio service agrees.
242 
243         int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0;
244         int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0;
245 
246         // See the statsd atoms.proto
247         int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0;
248         int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0;
249         int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
250     } mDeviceConnection{*this};
251 
252     // AAudioStreamInfo is a nested class which collect aaudio stream info from both client and
253     // server side.
254     class AAudioStreamInfo {
255     public:
256         // All the enum here must be kept the same as the ones defined in atoms.proto
257         enum CallerPath {
258             CALLER_PATH_UNKNOWN = 0,
259             CALLER_PATH_LEGACY = 1,
260             CALLER_PATH_MMAP = 2,
261         };
262 
AAudioStreamInfo(AudioAnalytics & audioAnalytics)263         explicit AAudioStreamInfo(AudioAnalytics &audioAnalytics)
264             : mAudioAnalytics(audioAnalytics) {}
265 
266         void endAAudioStream(
267                 const std::shared_ptr<const android::mediametrics::Item> &item,
268                 CallerPath path) const;
269 
270     private:
271 
272         AudioAnalytics &mAudioAnalytics;
273     } mAAudioStreamInfo{*this};
274 
275     // Create new state, typically occurs after an AudioFlinger ctor event.
276     void newState();
277 
278     // Health is a nested class that tracks audioserver health properties
279     class Health {
280     public:
Health(AudioAnalytics & audioAnalytics)281         explicit Health(AudioAnalytics &audioAnalytics)
282             : mAudioAnalytics(audioAnalytics) {}
283 
284         enum class Module {
285             AUDIOFLINGER,
286             AUDIOPOLICY,
287         };
288 
getModuleName(Module module)289         const char *getModuleName(Module module) {
290             switch (module) {
291                 case Module::AUDIOFLINGER: return "AudioFlinger";
292                 case Module::AUDIOPOLICY: return "AudioPolicy";
293             }
294             return "Unknown";
295         }
296 
297         // Called when we believe audioserver starts (AudioFlinger ctor)
298         void onAudioServerStart(Module module,
299                 const std::shared_ptr<const android::mediametrics::Item> &item);
300 
301         // Called when we believe audioserver crashes (TimeCheck timeouts).
302         void onAudioServerTimeout(Module module,
303                 const std::shared_ptr<const android::mediametrics::Item> &item);
304 
305         std::pair<std::string, int32_t> dump(
306                 int32_t lines = INT32_MAX, const char *prefix = nullptr) const;
307 
308     private:
309         AudioAnalytics& mAudioAnalytics;
310 
311         mutable std::mutex mLock;
312 
313         // Life cycle of AudioServer
314         // mAudioFlingerCtorTime
315         // mAudioPolicyCtorTime
316         // mAudioPolicyCtorDoneTime
317         // ...
318         // possibly mStopTime  (if TimeCheck thread)
319         //
320         // UpTime is measured from mStopTime - mAudioFlingerCtorTime.
321         //
322         // The stop events come from TimeCheck timeout aborts.  There may be other
323         // uncaught signals, e.g. SIGSEGV, that cause missing stop events.
324         std::chrono::system_clock::time_point mAudioFlingerCtorTime GUARDED_BY(mLock);
325         std::chrono::system_clock::time_point mAudioPolicyCtorTime GUARDED_BY(mLock);
326         std::chrono::system_clock::time_point mAudioPolicyCtorDoneTime GUARDED_BY(mLock);
327         std::chrono::system_clock::time_point mStopTime GUARDED_BY(mLock);
328 
329         // mStartCount and mStopCount track the audioserver start and stop events.
330         int64_t mStartCount GUARDED_BY(mLock) = 0;
331         int64_t mStopCount GUARDED_BY(mLock) = 0;
332 
GUARDED_BY(mLock)333         SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
334     } mHealth{*this};
335 
336     // Spatializer is a nested class that tracks related messages.
337     class Spatializer {
338     public:
Spatializer(AudioAnalytics & audioAnalytics)339         explicit Spatializer(AudioAnalytics &audioAnalytics)
340             : mAudioAnalytics(audioAnalytics) {}
341 
342         // an item that starts with "audio.spatializer"
343         void onEvent(const std::shared_ptr<const android::mediametrics::Item> &item);
344 
345         std::pair<std::string, int32_t> dump(
346                 int32_t lines = INT32_MAX, const char *prefix = nullptr) const;
347 
348     private:
349 
350         // Current device state as strings:
351         // "" means unknown, "true" or "false".
352         struct DeviceState {
353             std::string enabled;
354             std::string hasHeadTracker;
355             std::string headTrackerEnabled;
356         };
357 
358         AudioAnalytics& mAudioAnalytics;
359         static constexpr int64_t kBootDurationThreshold = 120 /* seconds */ * 1e9;
360         mutable std::mutex mLock;
361         int64_t mFirstCreateTimeNs GUARDED_BY(mLock) = 0;
362         std::map<std::string, DeviceState> mDeviceStateMap GUARDED_BY(mLock);
GUARDED_BY(mLock)363         SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
364     } mSpatializer{*this};
365 
366     AudioPowerUsage mAudioPowerUsage;
367 };
368 
369 } // namespace android::mediametrics
370