Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 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 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "AudioAnalytics" |
| 19 | #include <utils/Log.h> |
| 20 | |
| 21 | #include "AudioAnalytics.h" |
Andy Hung | ce9b663 | 2020-04-28 20:15:17 -0700 | [diff] [blame] | 22 | #include "MediaMetricsService.h" // package info |
| 23 | #include <audio_utils/clock.h> // clock conversions |
| 24 | #include <statslog.h> // statsd |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 25 | |
Andy Hung | ce9b663 | 2020-04-28 20:15:17 -0700 | [diff] [blame] | 26 | // Enable for testing of delivery to statsd |
| 27 | // #define STATSD |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 28 | |
| 29 | namespace android::mediametrics { |
| 30 | |
| 31 | AudioAnalytics::AudioAnalytics() |
| 32 | { |
| 33 | ALOGD("%s", __func__); |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 34 | |
| 35 | // Add action to save AnalyticsState if audioserver is restarted. |
| 36 | // This triggers on an item of "audio.flinger" |
| 37 | // with a property "event" set to "AudioFlinger" (the constructor). |
| 38 | mActions.addAction( |
Andy Hung | ea186fa | 2020-01-09 18:13:15 -0800 | [diff] [blame] | 39 | AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT, |
| 40 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR), |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 41 | std::make_shared<AnalyticsActions::Function>( |
Andy Hung | ea186fa | 2020-01-09 18:13:15 -0800 | [diff] [blame] | 42 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 43 | ALOGW("(key=%s) Audioflinger constructor event detected", item->getKey().c_str()); |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 44 | mPreviousAnalyticsState.set(std::make_shared<AnalyticsState>( |
| 45 | *mAnalyticsState.get())); |
| 46 | // Note: get returns shared_ptr temp, whose lifetime is extended |
| 47 | // to end of full expression. |
| 48 | mAnalyticsState->clear(); // TODO: filter the analytics state. |
Andy Hung | ea186fa | 2020-01-09 18:13:15 -0800 | [diff] [blame] | 49 | // Perhaps report this. |
| 50 | })); |
| 51 | |
| 52 | // Check underruns |
| 53 | mActions.addAction( |
| 54 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT, |
| 55 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN), |
| 56 | std::make_shared<AnalyticsActions::Function>( |
| 57 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 58 | std::string threadId = item->getKey().substr( |
| 59 | sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) - 1); |
| 60 | std::string outputDevices; |
| 61 | mAnalyticsState->timeMachine().get( |
| 62 | item->getKey(), AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices); |
| 63 | ALOGD("(key=%s) Thread underrun event detected on io handle:%s device:%s", |
| 64 | item->getKey().c_str(), threadId.c_str(), outputDevices.c_str()); |
| 65 | if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) { |
| 66 | // report this for Bluetooth |
| 67 | } |
| 68 | })); |
| 69 | |
| 70 | // Check latencies, playback and startup |
| 71 | mActions.addAction( |
| 72 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_LATENCYMS, |
| 73 | std::monostate{}, // accept any value |
| 74 | std::make_shared<AnalyticsActions::Function>( |
| 75 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 76 | double latencyMs{}; |
| 77 | double startupMs{}; |
| 78 | if (!item->get(AMEDIAMETRICS_PROP_LATENCYMS, &latencyMs) |
| 79 | || !item->get(AMEDIAMETRICS_PROP_STARTUPMS, &startupMs)) return; |
| 80 | |
| 81 | std::string trackId = item->getKey().substr( |
| 82 | sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) - 1); |
| 83 | std::string thread = getThreadFromTrack(item->getKey()); |
| 84 | std::string outputDevices; |
| 85 | mAnalyticsState->timeMachine().get( |
| 86 | thread, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices); |
| 87 | ALOGD("(key=%s) Track latencyMs:%lf startupMs:%lf detected on port:%s device:%s", |
| 88 | item->getKey().c_str(), latencyMs, startupMs, |
| 89 | trackId.c_str(), outputDevices.c_str()); |
| 90 | if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) { |
| 91 | // report this for Bluetooth |
| 92 | } |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 93 | })); |
Andy Hung | ce9b663 | 2020-04-28 20:15:17 -0700 | [diff] [blame] | 94 | |
| 95 | // Handle device use thread statistics |
| 96 | mActions.addAction( |
| 97 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT, |
| 98 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP), |
| 99 | std::make_shared<AnalyticsActions::Function>( |
| 100 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 101 | mDeviceUse.endAudioIntervalGroup(item, false /* isTrack */); |
| 102 | })); |
| 103 | |
| 104 | // Handle device use track statistics |
| 105 | mActions.addAction( |
| 106 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT, |
| 107 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP), |
| 108 | std::make_shared<AnalyticsActions::Function>( |
| 109 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 110 | mDeviceUse.endAudioIntervalGroup(item, true /* isTrack */); |
| 111 | })); |
| 112 | |
| 113 | // Handle device routing statistics |
| 114 | |
| 115 | // We track connections (not disconnections) for the time to connect. |
| 116 | // TODO: consider BT requests in their A2dp service |
| 117 | // AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent |
| 118 | // AudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent |
| 119 | // AudioDeviceBroker.postA2dpActiveDeviceChange |
| 120 | mActions.addAction( |
| 121 | "audio.device.a2dp.state", |
| 122 | std::string("connected"), |
| 123 | std::make_shared<AnalyticsActions::Function>( |
| 124 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 125 | mDeviceConnection.a2dpConnected(item); |
| 126 | })); |
| 127 | // If audio is active, we expect to see a createAudioPatch after the device is connected. |
| 128 | mActions.addAction( |
| 129 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT, |
| 130 | std::string("createAudioPatch"), |
| 131 | std::make_shared<AnalyticsActions::Function>( |
| 132 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 133 | mDeviceConnection.createPatch(item); |
| 134 | })); |
Joey Poomarin | 5298998 | 2020-03-05 17:40:49 +0800 | [diff] [blame^] | 135 | |
| 136 | // Handle power usage |
| 137 | mActions.addAction( |
| 138 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT, |
| 139 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP), |
| 140 | std::make_shared<AnalyticsActions::Function>( |
| 141 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 142 | mAudioPowerUsage.checkTrackRecord(item, true /* isTrack */); |
| 143 | })); |
| 144 | |
| 145 | mActions.addAction( |
| 146 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT, |
| 147 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP), |
| 148 | std::make_shared<AnalyticsActions::Function>( |
| 149 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 150 | mAudioPowerUsage.checkTrackRecord(item, false /* isTrack */); |
| 151 | })); |
| 152 | |
| 153 | mActions.addAction( |
| 154 | AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT, |
| 155 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE), |
| 156 | std::make_shared<AnalyticsActions::Function>( |
| 157 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 158 | // ALOGD("(key=%s) Audioflinger setMode", item->getKey().c_str()); |
| 159 | mAudioPowerUsage.checkMode(item); |
| 160 | })); |
| 161 | |
| 162 | mActions.addAction( |
| 163 | AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT, |
| 164 | std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME), |
| 165 | std::make_shared<AnalyticsActions::Function>( |
| 166 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 167 | // ALOGD("(key=%s) Audioflinger setVoiceVolume", item->getKey().c_str()); |
| 168 | mAudioPowerUsage.checkVoiceVolume(item); |
| 169 | })); |
| 170 | |
| 171 | mActions.addAction( |
| 172 | AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT, |
| 173 | std::string("createAudioPatch"), |
| 174 | std::make_shared<AnalyticsActions::Function>( |
| 175 | [this](const std::shared_ptr<const android::mediametrics::Item> &item){ |
| 176 | mAudioPowerUsage.checkCreatePatch(item); |
| 177 | })); |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | AudioAnalytics::~AudioAnalytics() |
| 181 | { |
| 182 | ALOGD("%s", __func__); |
Andy Hung | ce9b663 | 2020-04-28 20:15:17 -0700 | [diff] [blame] | 183 | mTimedAction.quit(); // ensure no deferred access during destructor. |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | status_t AudioAnalytics::submit( |
Ray Essick | f27e987 | 2019-12-07 06:28:46 -0800 | [diff] [blame] | 187 | const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted) |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 188 | { |
Andy Hung | ea186fa | 2020-01-09 18:13:15 -0800 | [diff] [blame] | 189 | if (!startsWith(item->getKey(), AMEDIAMETRICS_KEY_PREFIX_AUDIO)) return BAD_VALUE; |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 190 | status_t status = mAnalyticsState->submit(item, isTrusted); |
| 191 | if (status != NO_ERROR) return status; // may not be permitted. |
| 192 | |
| 193 | // Only if the item was successfully submitted (permission) |
| 194 | // do we check triggered actions. |
| 195 | checkActions(item); |
| 196 | return NO_ERROR; |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 197 | } |
| 198 | |
Andy Hung | 709b91e | 2020-04-04 14:23:36 -0700 | [diff] [blame] | 199 | std::pair<std::string, int32_t> AudioAnalytics::dump( |
| 200 | int32_t lines, int64_t sinceNs, const char *prefix) const |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 201 | { |
| 202 | std::stringstream ss; |
| 203 | int32_t ll = lines; |
| 204 | |
| 205 | if (ll > 0) { |
Andy Hung | 709b91e | 2020-04-04 14:23:36 -0700 | [diff] [blame] | 206 | auto [s, l] = mAnalyticsState->dump(ll, sinceNs, prefix); |
Andy Hung | b744faf | 2020-04-09 13:09:26 -0700 | [diff] [blame] | 207 | ss << s; |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 208 | ll -= l; |
| 209 | } |
| 210 | if (ll > 0) { |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 211 | ss << "Prior audioserver state:\n"; |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 212 | --ll; |
| 213 | } |
| 214 | if (ll > 0) { |
Andy Hung | 709b91e | 2020-04-04 14:23:36 -0700 | [diff] [blame] | 215 | auto [s, l] = mPreviousAnalyticsState->dump(ll, sinceNs, prefix); |
Andy Hung | b744faf | 2020-04-09 13:09:26 -0700 | [diff] [blame] | 216 | ss << s; |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 217 | ll -= l; |
| 218 | } |
Joey Poomarin | 5298998 | 2020-03-05 17:40:49 +0800 | [diff] [blame^] | 219 | |
| 220 | if (ll > 0 && prefix == nullptr) { |
| 221 | auto [s, l] = mAudioPowerUsage.dump(ll); |
| 222 | ss << s; |
| 223 | ll -= l; |
| 224 | } |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 225 | return { ss.str(), lines - ll }; |
| 226 | } |
| 227 | |
Andy Hung | 0f7ad8c | 2020-01-03 13:24:34 -0800 | [diff] [blame] | 228 | void AudioAnalytics::checkActions(const std::shared_ptr<const mediametrics::Item>& item) |
| 229 | { |
| 230 | auto actions = mActions.getActionsForItem(item); // internally locked. |
| 231 | // Execute actions with no lock held. |
| 232 | for (const auto& action : actions) { |
| 233 | (*action)(item); |
| 234 | } |
| 235 | } |
| 236 | |
Andy Hung | ea186fa | 2020-01-09 18:13:15 -0800 | [diff] [blame] | 237 | // HELPER METHODS |
| 238 | |
| 239 | std::string AudioAnalytics::getThreadFromTrack(const std::string& track) const |
| 240 | { |
| 241 | int32_t threadId_int32{}; |
| 242 | if (mAnalyticsState->timeMachine().get( |
| 243 | track, AMEDIAMETRICS_PROP_THREADID, &threadId_int32) != NO_ERROR) { |
| 244 | return {}; |
| 245 | } |
| 246 | return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(threadId_int32); |
| 247 | } |
| 248 | |
Andy Hung | ce9b663 | 2020-04-28 20:15:17 -0700 | [diff] [blame] | 249 | // DeviceUse helper class. |
| 250 | void AudioAnalytics::DeviceUse::endAudioIntervalGroup( |
| 251 | const std::shared_ptr<const android::mediametrics::Item> &item, bool isTrack) const { |
| 252 | const std::string& key = item->getKey(); |
| 253 | const std::string id = key.substr( |
| 254 | (isTrack ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) |
| 255 | : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD)) |
| 256 | - 1); |
| 257 | // deliver statistics |
| 258 | int64_t deviceTimeNs = 0; |
| 259 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 260 | key, AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs); |
| 261 | std::string encoding; |
| 262 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 263 | key, AMEDIAMETRICS_PROP_ENCODING, &encoding); |
| 264 | int32_t frameCount = 0; |
| 265 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 266 | key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount); |
| 267 | int32_t intervalCount = 0; |
| 268 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 269 | key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount); |
| 270 | std::string outputDevices; |
| 271 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 272 | key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices); |
| 273 | int32_t sampleRate = 0; |
| 274 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 275 | key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate); |
| 276 | int32_t underrun = 0; |
| 277 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 278 | key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun); |
| 279 | |
| 280 | // Get connected device name if from bluetooth. |
| 281 | bool isBluetooth = false; |
| 282 | std::string name; |
| 283 | if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) { |
| 284 | isBluetooth = true; |
| 285 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 286 | "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &name); |
| 287 | } |
| 288 | |
| 289 | // We may have several devices. We only list the first device. |
| 290 | // TODO: consider whether we should list all the devices separated by | |
| 291 | std::string firstDevice = "unknown"; |
| 292 | auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(outputDevices); |
| 293 | if (devaddrvec.size() != 0) { |
| 294 | firstDevice = devaddrvec[0].first; |
| 295 | // DO NOT show the address. |
| 296 | } |
| 297 | |
| 298 | if (isTrack) { |
| 299 | std::string callerName; |
| 300 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 301 | key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName); |
| 302 | std::string contentType; |
| 303 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 304 | key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentType); |
| 305 | double deviceLatencyMs = 0.; |
| 306 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 307 | key, AMEDIAMETRICS_PROP_DEVICELATENCYMS, &deviceLatencyMs); |
| 308 | double deviceStartupMs = 0.; |
| 309 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 310 | key, AMEDIAMETRICS_PROP_DEVICESTARTUPMS, &deviceStartupMs); |
| 311 | double deviceVolume = 0.; |
| 312 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 313 | key, AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume); |
| 314 | std::string packageName; |
| 315 | int64_t versionCode = 0; |
| 316 | int32_t uid = -1; |
| 317 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 318 | key, AMEDIAMETRICS_PROP_ALLOWUID, &uid); |
| 319 | if (uid != -1) { |
| 320 | std::tie(packageName, versionCode) = |
| 321 | MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid); |
| 322 | } |
| 323 | double playbackPitch = 0.; |
| 324 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 325 | key, AMEDIAMETRICS_PROP_PLAYBACK_PITCH, &playbackPitch); |
| 326 | double playbackSpeed = 0.; |
| 327 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 328 | key, AMEDIAMETRICS_PROP_PLAYBACK_SPEED, &playbackSpeed); |
| 329 | int32_t selectedDeviceId = 0; |
| 330 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 331 | key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId); |
| 332 | |
| 333 | std::string usage; |
| 334 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 335 | key, AMEDIAMETRICS_PROP_USAGE, &usage); |
| 336 | |
| 337 | ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%s " |
| 338 | "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d " |
| 339 | "sampleRate:%d underrun:%d " |
| 340 | "callerName:%s contentType:%s " |
| 341 | "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf" |
| 342 | "packageName:%s playbackPitch:%lf playbackSpeed:%lf " |
| 343 | "selectedDevceId:%d usage:%s", |
| 344 | key.c_str(), id.c_str(), firstDevice.c_str(), name.c_str(), |
| 345 | (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount, |
| 346 | sampleRate, underrun, |
| 347 | callerName.c_str(), contentType.c_str(), |
| 348 | deviceLatencyMs, deviceStartupMs, deviceVolume, |
| 349 | packageName.c_str(), playbackPitch, playbackSpeed, |
| 350 | selectedDeviceId, usage.c_str()); |
| 351 | #ifdef STATSD |
| 352 | if (mAudioAnalytics.mDeliverStatistics) { |
| 353 | (void)android::util::stats_write( |
| 354 | android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED |
| 355 | /* timestamp, */ |
| 356 | /* mediaApexVersion, */ |
| 357 | , firstDevice.c_str() |
| 358 | , name.c_str() |
| 359 | , deviceTimeNs |
| 360 | , encoding.c_str() |
| 361 | , frameCount |
| 362 | , intervalCount |
| 363 | , sampleRate |
| 364 | , underrun |
| 365 | |
| 366 | , packageName.c_str() |
| 367 | , (float)deviceLatencyMs |
| 368 | , (float)deviceStartupMs |
| 369 | , (float)deviceVolume |
| 370 | , selectedDeviceId |
| 371 | , usage.c_str() |
| 372 | , contentType.c_str() |
| 373 | , callerName.c_str() |
| 374 | ); |
| 375 | } |
| 376 | #endif |
| 377 | } else { |
| 378 | |
| 379 | std::string flags; |
| 380 | mAudioAnalytics.mAnalyticsState->timeMachine().get( |
| 381 | key, AMEDIAMETRICS_PROP_FLAGS, &flags); |
| 382 | |
| 383 | ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%s " |
| 384 | "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d " |
| 385 | "sampleRate:%d underrun:%d " |
| 386 | "flags:%s", |
| 387 | key.c_str(), id.c_str(), firstDevice.c_str(), name.c_str(), |
| 388 | (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount, |
| 389 | sampleRate, underrun, |
| 390 | flags.c_str()); |
| 391 | #ifdef STATSD |
| 392 | if (mAudioAnalytics.mDeliverStatistics) { |
| 393 | (void)android::util::stats_write( |
| 394 | android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED |
| 395 | /* timestamp, */ |
| 396 | /* mediaApexVersion, */ |
| 397 | , firstDevice.c_str() |
| 398 | , name.c_str() |
| 399 | , deviceTimeNs |
| 400 | , encoding.c_str() |
| 401 | , frameCount |
| 402 | , intervalCount |
| 403 | , sampleRate |
| 404 | , underrun |
| 405 | ); |
| 406 | } |
| 407 | #endif |
| 408 | } |
| 409 | |
| 410 | // Report this as needed. |
| 411 | if (isBluetooth) { |
| 412 | // report this for Bluetooth |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // DeviceConnection helper class. |
| 417 | void AudioAnalytics::DeviceConnection::a2dpConnected( |
| 418 | const std::shared_ptr<const android::mediametrics::Item> &item) { |
| 419 | const std::string& key = item->getKey(); |
| 420 | |
| 421 | const int64_t connectedAtNs = item->getTimestamp(); |
| 422 | { |
| 423 | std::lock_guard l(mLock); |
| 424 | mA2dpTimeConnectedNs = connectedAtNs; |
| 425 | ++mA2dpConnectedAttempts; |
| 426 | } |
| 427 | std::string name; |
| 428 | item->get(AMEDIAMETRICS_PROP_NAME, &name); |
| 429 | ALOGD("(key=%s) a2dp connected device:%s " |
| 430 | "connectedAtNs:%lld", |
| 431 | key.c_str(), name.c_str(), |
| 432 | (long long)connectedAtNs); |
| 433 | // Note - we need to be able to cancel a timed event |
| 434 | mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); }); |
| 435 | // This sets the time we were connected. Now we look for the delta in the future. |
| 436 | } |
| 437 | |
| 438 | void AudioAnalytics::DeviceConnection::createPatch( |
| 439 | const std::shared_ptr<const android::mediametrics::Item> &item) { |
| 440 | std::lock_guard l(mLock); |
| 441 | if (mA2dpTimeConnectedNs == 0) return; // ignore |
| 442 | const std::string& key = item->getKey(); |
| 443 | std::string outputDevices; |
| 444 | item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices); |
| 445 | if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) { |
| 446 | // TODO compare address |
| 447 | const int64_t timeDiff = item->getTimestamp() - mA2dpTimeConnectedNs; |
| 448 | ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff); |
| 449 | mA2dpTimeConnectedNs = 0; // reset counter. |
| 450 | ++mA2dpConnectedSuccesses; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | void AudioAnalytics::DeviceConnection::expire() { |
| 455 | std::lock_guard l(mLock); |
| 456 | if (mA2dpTimeConnectedNs == 0) return; // ignore |
| 457 | |
| 458 | // An expiration may occur because there is no audio playing. |
| 459 | // TODO: disambiguate this case. |
| 460 | ALOGD("A2DP device connection expired"); |
| 461 | ++mA2dpConnectedFailures; // this is not a true failure. |
| 462 | mA2dpTimeConnectedNs = 0; |
| 463 | } |
| 464 | |
Andy Hung | 06f3aba | 2019-12-03 16:36:42 -0800 | [diff] [blame] | 465 | } // namespace android |