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