• Home
  • History
  • Annotate
  • 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      // MidiLogging collects info whenever a MIDI device is closed.
367      class MidiLogging {
368      public:
MidiLogging(AudioAnalytics & audioAnalytics)369          explicit MidiLogging(AudioAnalytics &audioAnalytics)
370              : mAudioAnalytics(audioAnalytics) {}
371  
372          void onEvent(
373                  const std::shared_ptr<const android::mediametrics::Item> &item) const;
374  
375      private:
376  
377          AudioAnalytics &mAudioAnalytics;
378      } mMidiLogging{*this};
379  
380      AudioPowerUsage mAudioPowerUsage;
381  };
382  
383  } // namespace android::mediametrics
384