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 "StatsdLog.h" 24 #include "TimedAction.h" 25 #include "Wrap.h" 26 27 namespace android::mediametrics { 28 29 class AudioAnalytics 30 { 31 // AudioAnalytics action / state helper classes 32 friend AudioPowerUsage; 33 34 public: 35 explicit AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog); 36 ~AudioAnalytics(); 37 38 /** 39 * Returns success if AudioAnalytics recognizes item. 40 * 41 * AudioAnalytics requires the item key to start with "audio.". 42 * 43 * A trusted source can create a new key, an untrusted source 44 * can only modify the key if the uid will match that authorized 45 * on the existing key. 46 * 47 * \param item the item to be submitted. 48 * \param isTrusted whether the transaction comes from a trusted source. 49 * In this case, a trusted source is verified by binder 50 * UID to be a system service by MediaMetrics service. 51 * Do not use true if you haven't really checked! 52 * 53 * \return NO_ERROR on success, 54 * PERMISSION_DENIED if the item cannot be put into the AnalyticsState, 55 * BAD_VALUE if the item key does not start with "audio.". 56 */ 57 status_t submit(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted); 58 59 /** 60 * Returns a pair consisting of the dump string, and the number of lines in the string. 61 * 62 * The number of lines in the returned pair is used as an optimization 63 * for subsequent line limiting. 64 * 65 * The TimeMachine and the TransactionLog are dumped separately under 66 * different locks, so may not be 100% consistent with the last data 67 * delivered. 68 * 69 * \param lines the maximum number of lines in the string returned. 70 * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all) 71 * \param prefix the desired key prefix to match (nullptr shows all) 72 */ 73 std::pair<std::string, int32_t> dump( 74 int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const; 75 clear()76 void clear() { 77 // underlying state is locked. 78 mPreviousAnalyticsState->clear(); 79 mAnalyticsState->clear(); 80 81 // Clear power usage state. 82 mAudioPowerUsage.clear(); 83 } 84 85 private: 86 87 /* 88 * AudioAnalytics class does not contain a monitor mutex. 89 * Instead, all of its variables are individually locked for access. 90 * Since data and items are generally added only (gc removes it), this is a reasonable 91 * compromise for availability/concurrency versus consistency. 92 * 93 * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics. 94 * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be 95 * used to achieve better consistency if needed. 96 */ 97 98 /** 99 * Checks for any pending actions for a particular item. 100 * 101 * \param item to check against the current AnalyticsActions. 102 */ 103 void checkActions(const std::shared_ptr<const mediametrics::Item>& item); 104 105 // HELPER METHODS 106 /** 107 * Return the audio thread associated with an audio track name. 108 * e.g. "audio.track.32" -> "audio.thread.10" if the associated 109 * threadId for the audio track is 10. 110 */ 111 std::string getThreadFromTrack(const std::string& track) const; 112 113 const bool mDeliverStatistics; 114 115 // Actions is individually locked 116 AnalyticsActions mActions; 117 118 // AnalyticsState is individually locked, and we use SharedPtrWrap 119 // to allow safe access even if the shared pointer changes underneath. 120 // These wrap pointers always point to a valid state object. 121 SharedPtrWrap<AnalyticsState> mAnalyticsState; 122 SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState; 123 124 TimedAction mTimedAction; // locked internally 125 const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads. 126 127 // DeviceUse is a nested class which handles audio device usage accounting. 128 // We define this class at the end to ensure prior variables all properly constructed. 129 // TODO: Track / Thread interaction 130 // TODO: Consider statistics aggregation. 131 class DeviceUse { 132 public: 133 enum ItemType { 134 RECORD = 0, 135 THREAD = 1, 136 TRACK = 2, 137 }; 138 DeviceUse(AudioAnalytics & audioAnalytics)139 explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {} 140 141 // Called every time an endAudioIntervalGroup message is received. 142 void endAudioIntervalGroup( 143 const std::shared_ptr<const android::mediametrics::Item> &item, 144 ItemType itemType) const; 145 146 private: 147 AudioAnalytics &mAudioAnalytics; 148 } mDeviceUse{*this}; 149 150 // DeviceConnected is a nested class which handles audio device connection 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 DeviceConnection { 155 public: DeviceConnection(AudioAnalytics & audioAnalytics)156 explicit DeviceConnection(AudioAnalytics &audioAnalytics) 157 : mAudioAnalytics{audioAnalytics} {} 158 159 // Called every time an endAudioIntervalGroup message is received. 160 void a2dpConnected( 161 const std::shared_ptr<const android::mediametrics::Item> &item); 162 163 // Called when we have an AudioFlinger createPatch 164 void createPatch( 165 const std::shared_ptr<const android::mediametrics::Item> &item); 166 167 // Called through AudioManager when the BT service wants to notify connection 168 void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( 169 const std::shared_ptr<const android::mediametrics::Item> &item); 170 171 // When the timer expires. 172 void expire(); 173 174 private: 175 AudioAnalytics &mAudioAnalytics; 176 177 mutable std::mutex mLock; 178 std::string mA2dpDeviceName; 179 int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0; // Time for BT service request. 180 int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0; // Time audio service agrees. 181 182 int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0; 183 int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0; 184 185 // See the statsd atoms.proto 186 int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0; 187 int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0; 188 int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0; 189 } mDeviceConnection{*this}; 190 191 // AAudioStreamInfo is a nested class which collect aaudio stream info from both client and 192 // server side. 193 class AAudioStreamInfo { 194 public: 195 // All the enum here must be kept the same as the ones defined in atoms.proto 196 enum CallerPath { 197 CALLER_PATH_UNKNOWN = 0, 198 CALLER_PATH_LEGACY = 1, 199 CALLER_PATH_MMAP = 2, 200 }; 201 AAudioStreamInfo(AudioAnalytics & audioAnalytics)202 explicit AAudioStreamInfo(AudioAnalytics &audioAnalytics) 203 : mAudioAnalytics(audioAnalytics) {} 204 205 void endAAudioStream( 206 const std::shared_ptr<const android::mediametrics::Item> &item, 207 CallerPath path) const; 208 209 private: 210 211 AudioAnalytics &mAudioAnalytics; 212 } mAAudioStreamInfo{*this}; 213 214 AudioPowerUsage mAudioPowerUsage; 215 }; 216 217 } // namespace android::mediametrics 218