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