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