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 clear()86 void clear() { 87 // underlying state is locked. 88 mPreviousAnalyticsState->clear(); 89 mAnalyticsState->clear(); 90 91 // Clears the status map 92 mHeatMap.clear(); 93 94 // Clear power usage state. 95 mAudioPowerUsage.clear(); 96 } 97 98 private: 99 100 /* 101 * AudioAnalytics class does not contain a monitor mutex. 102 * Instead, all of its variables are individually locked for access. 103 * Since data and items are generally added only (gc removes it), this is a reasonable 104 * compromise for availability/concurrency versus consistency. 105 * 106 * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics. 107 * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be 108 * used to achieve better consistency if needed. 109 */ 110 111 /** 112 * Processes any pending actions for a particular item. 113 * 114 * \param item to check against the current AnalyticsActions. 115 */ 116 void processActions(const std::shared_ptr<const mediametrics::Item>& item); 117 118 /** 119 * Processes status information contained in the item. 120 * 121 * \param item to check against for status handling 122 */ 123 void processStatus(const std::shared_ptr<const mediametrics::Item>& item); 124 125 // HELPER METHODS 126 /** 127 * Return the audio thread associated with an audio track name. 128 * e.g. "audio.track.32" -> "audio.thread.10" if the associated 129 * threadId for the audio track is 10. 130 */ 131 std::string getThreadFromTrack(const std::string& track) const; 132 133 const bool mDeliverStatistics; 134 135 // Actions is individually locked 136 AnalyticsActions mActions; 137 138 // AnalyticsState is individually locked, and we use SharedPtrWrap 139 // to allow safe access even if the shared pointer changes underneath. 140 // These wrap pointers always point to a valid state object. 141 SharedPtrWrap<AnalyticsState> mAnalyticsState; 142 SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState; 143 144 TimedAction mTimedAction; // locked internally 145 const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads. 146 147 static constexpr size_t kHeatEntries = 100; 148 HeatMap mHeatMap{kHeatEntries}; // locked internally, ok for multiple threads. 149 150 // DeviceUse is a nested class which handles audio device usage accounting. 151 // We define this class at the end to ensure prior variables all properly constructed. 152 // TODO: Track / Thread interaction 153 // TODO: Consider statistics aggregation. 154 class DeviceUse { 155 public: 156 enum ItemType { 157 RECORD = 0, 158 THREAD = 1, 159 TRACK = 2, 160 }; 161 DeviceUse(AudioAnalytics & audioAnalytics)162 explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {} 163 164 // Called every time an endAudioIntervalGroup message is received. 165 void endAudioIntervalGroup( 166 const std::shared_ptr<const android::mediametrics::Item> &item, 167 ItemType itemType) const; 168 169 private: 170 AudioAnalytics &mAudioAnalytics; 171 } mDeviceUse{*this}; 172 173 // DeviceConnected is a nested class which handles audio device connection 174 // We define this class at the end to ensure prior variables all properly constructed. 175 // TODO: Track / Thread interaction 176 // TODO: Consider statistics aggregation. 177 class DeviceConnection { 178 public: DeviceConnection(AudioAnalytics & audioAnalytics)179 explicit DeviceConnection(AudioAnalytics &audioAnalytics) 180 : mAudioAnalytics{audioAnalytics} {} 181 182 // Called every time an endAudioIntervalGroup message is received. 183 void a2dpConnected( 184 const std::shared_ptr<const android::mediametrics::Item> &item); 185 186 // Called when we have an AudioFlinger createPatch 187 void createPatch( 188 const std::shared_ptr<const android::mediametrics::Item> &item); 189 190 // Called through AudioManager when the BT service wants to notify connection 191 void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( 192 const std::shared_ptr<const android::mediametrics::Item> &item); 193 194 // When the timer expires. 195 void expire(); 196 197 private: 198 AudioAnalytics &mAudioAnalytics; 199 200 mutable std::mutex mLock; 201 std::string mA2dpDeviceName; 202 int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0; // Time for BT service request. 203 int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0; // Time audio service agrees. 204 205 int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0; 206 int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0; 207 208 // See the statsd atoms.proto 209 int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0; 210 int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0; 211 int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0; 212 } mDeviceConnection{*this}; 213 214 // AAudioStreamInfo is a nested class which collect aaudio stream info from both client and 215 // server side. 216 class AAudioStreamInfo { 217 public: 218 // All the enum here must be kept the same as the ones defined in atoms.proto 219 enum CallerPath { 220 CALLER_PATH_UNKNOWN = 0, 221 CALLER_PATH_LEGACY = 1, 222 CALLER_PATH_MMAP = 2, 223 }; 224 AAudioStreamInfo(AudioAnalytics & audioAnalytics)225 explicit AAudioStreamInfo(AudioAnalytics &audioAnalytics) 226 : mAudioAnalytics(audioAnalytics) {} 227 228 void endAAudioStream( 229 const std::shared_ptr<const android::mediametrics::Item> &item, 230 CallerPath path) const; 231 232 private: 233 234 AudioAnalytics &mAudioAnalytics; 235 } mAAudioStreamInfo{*this}; 236 237 AudioPowerUsage mAudioPowerUsage; 238 }; 239 240 } // namespace android::mediametrics 241