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 <android-base/logging.h>
20 #include <utils/Log.h>
21 
22 #include "AudioAnalytics.h"
23 
24 #include <aaudio/AAudio.h>        // error codes
25 #include <audio_utils/clock.h>    // clock conversions
26 #include <cutils/properties.h>
27 #include <statslog.h>             // statsd
28 #include <system/audio.h>
29 
30 #include "AudioTypes.h"           // string to int conversions
31 #include "MediaMetricsService.h"  // package info
32 #include "StringUtils.h"
33 #include "ValidateId.h"
34 
35 #define PROP_AUDIO_ANALYTICS_CLOUD_ENABLED "persist.audio.analytics.cloud.enabled"
36 
37 namespace android::mediametrics {
38 
39 // Enable for testing of delivery to statsd. Caution if this is enabled, all protos MUST exist.
40 #define STATSD_ENABLE
41 
42 #ifdef STATSD_ENABLE
43 #define CONDITION(INT_VALUE) (INT_VALUE)  // allow value
44 #else
45 #define CONDITION(INT_VALUE) (int(0))     // mask value since the proto may not be defined yet.
46 #endif
47 
48 // Maximum length of a device name.
49 // static constexpr size_t STATSD_DEVICE_NAME_MAX_LENGTH = 32; // unused since we suppress
50 
51 // Transmit Enums to statsd in integer or strings  (this must match the atoms.proto)
52 static constexpr bool STATSD_USE_INT_FOR_ENUM = false;
53 
54 // derive types based on integer or strings.
55 using short_enum_type_t = std::conditional_t<STATSD_USE_INT_FOR_ENUM, int32_t, std::string>;
56 using long_enum_type_t = std::conditional_t<STATSD_USE_INT_FOR_ENUM, int64_t, std::string>;
57 
58 // Convert std::string to char *
59 template <typename T>
ENUM_EXTRACT(const T & x)60 auto ENUM_EXTRACT(const T& x) {
61     if constexpr (std::is_same_v<std::decay_t<T>, std::string>) {
62         return x.c_str();
63     } else {
64         return x;
65     }
66 }
67 
68 // The status variable contains status_t codes which are used by
69 // the core audio framework.
70 //
71 // We also consider AAudio status codes as they are non-overlapping with status_t
72 // and compiler checked here.
73 //
74 // Caution: As AAUDIO_ERROR codes have a unique range (AAUDIO_ERROR_BASE = -900),
75 // overlap with status_t should not present an issue.
76 //
77 // See: system/core/libutils/include/utils/Errors.h
78 //      frameworks/av/media/libaaudio/include/aaudio/AAudio.h
79 //
80 // Compare with mediametrics::statusToStatusString
81 //
extendedStatusToStatusString(status_t status)82 inline constexpr const char* extendedStatusToStatusString(status_t status) {
83     switch (status) {
84     case BAD_VALUE:           // status_t
85     case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
86     case AAUDIO_ERROR_INVALID_FORMAT:
87     case AAUDIO_ERROR_INVALID_RATE:
88     case AAUDIO_ERROR_NULL:
89     case AAUDIO_ERROR_OUT_OF_RANGE:
90         return AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT;
91     case DEAD_OBJECT:         // status_t
92     case FAILED_TRANSACTION:  // status_t
93     case AAUDIO_ERROR_DISCONNECTED:
94     case AAUDIO_ERROR_INVALID_HANDLE:
95     case AAUDIO_ERROR_NO_SERVICE:
96         return AMEDIAMETRICS_PROP_STATUS_VALUE_IO;
97     case NO_MEMORY:           // status_t
98     case AAUDIO_ERROR_NO_FREE_HANDLES:
99     case AAUDIO_ERROR_NO_MEMORY:
100         return AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY;
101     case PERMISSION_DENIED:   // status_t
102         return AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY;
103     case INVALID_OPERATION:   // status_t
104     case NO_INIT:             // status_t
105     case AAUDIO_ERROR_INVALID_STATE:
106     case AAUDIO_ERROR_UNAVAILABLE:
107     case AAUDIO_ERROR_UNIMPLEMENTED:
108         return AMEDIAMETRICS_PROP_STATUS_VALUE_STATE;
109     case WOULD_BLOCK:         // status_t
110     case AAUDIO_ERROR_TIMEOUT:
111     case AAUDIO_ERROR_WOULD_BLOCK:
112         return AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT;
113     default:
114         if (status >= 0) return AMEDIAMETRICS_PROP_STATUS_VALUE_OK; // non-negative values "OK"
115         [[fallthrough]];            // negative values are error.
116     case UNKNOWN_ERROR:       // status_t
117         return AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN;
118     }
119 }
120 
121 static constexpr const auto LOG_LEVEL = android::base::VERBOSE;
122 
123 static constexpr int PREVIOUS_STATE_EXPIRE_SEC = 60 * 60; // 1 hour.
124 
125 static constexpr const char * SUPPRESSED = "SUPPRESSED";
126 
127 /*
128  * For logging purposes, we list all of the MediaMetrics atom fields,
129  * which can then be associated with consecutive arguments to the statsd write.
130  */
131 
132 static constexpr const char * const AudioRecordDeviceUsageFields[] = {
133     "mediametrics_audiorecorddeviceusage_reported", // proto number
134     "devices",
135     "device_names",
136     "device_time_nanos",
137     "encoding",
138     "frame_count",
139     "interval_count",
140     "sample_rate",
141     "flags",
142     "package_name",
143     "selected_device_id",
144     "caller",
145     "source",
146     "log_session_id",
147 };
148 
149 static constexpr const char * const AudioThreadDeviceUsageFields[] = {
150     "mediametrics_audiothreaddeviceusage_reported",
151     "devices",
152     "device_names",
153     "device_time_nanos",
154     "encoding",
155     "frame_count",
156     "interval_count",
157     "sample_rate",
158     "flags",
159     "xruns",
160     "type",
161 };
162 
163 static constexpr const char * const AudioTrackDeviceUsageFields[] = {
164     "mediametrics_audiotrackdeviceusage_reported",
165     "devices",
166     "device_names",
167     "device_time_nanos",
168     "encoding",
169     "frame_count",
170     "interval_count",
171     "sample_rate",
172     "flags",
173     "xruns",
174     "package_name",
175     "device_latency_millis",
176     "device_startup_millis",
177     "device_volume",
178     "selected_device_id",
179     "stream_type",
180     "usage",
181     "content_type",
182     "caller",
183     "traits",
184     "log_session_id",
185 };
186 
187 static constexpr const char * const AudioDeviceConnectionFields[] = {
188     "mediametrics_audiodeviceconnection_reported",
189     "input_devices",
190     "output_devices",
191     "device_names",
192     "result",
193     "time_to_connect_millis",
194     "connection_count",
195 };
196 
197 static constexpr const char * const AAudioStreamFields[] {
198     "mediametrics_aaudiostream_reported",
199     "path",
200     "direction",
201     "frames_per_burst",
202     "buffer_size",
203     "buffer_capacity",
204     "channel_count",
205     "total_frames_transferred",
206     "perf_mode_requested",
207     "perf_mode_actual",
208     "sharing",
209     "xrun_count",
210     "device_type",
211     "format_app",
212     "format_device",
213     "log_session_id",
214     "sample_rate",
215     "content_type",
216     "sharing_requested",
217 };
218 
219 /**
220  * printFields is a helper method that prints the fields and corresponding values
221  * in a human readable style.
222  */
223 template <size_t N, typename ...Types>
printFields(const char * const (& fields)[N],Types...args)224 std::string printFields(const char * const (& fields)[N], Types ... args)
225 {
226     std::stringstream ss;
227     ss << " { ";
228     stringutils::fieldPrint(ss, fields, args...);
229     ss << "}";
230     return ss.str();
231 }
232 
233 /**
234  * sendToStatsd is a helper method that sends the arguments to statsd
235  */
236 template <typename ...Types>
sendToStatsd(Types...args)237 int sendToStatsd(Types ... args)
238 {
239     int result = 0;
240 
241 #ifdef STATSD_ENABLE
242     result = android::util::stats_write(args...);
243 #endif
244     return result;
245 }
246 
247 /**
248  * sendToStatsd is a helper method that sends the arguments to statsd
249  * and returns a pair { result, summary_string }.
250  */
251 template <size_t N, typename ...Types>
sendToStatsd(const char * const (& fields)[N],Types...args)252 std::pair<int, std::string> sendToStatsd(const char * const (& fields)[N], Types ... args)
253 {
254     int result = 0;
255     std::stringstream ss;
256 
257 #ifdef STATSD_ENABLE
258     result = android::util::stats_write(args...);
259     ss << "result:" << result;
260 #endif
261     ss << " { ";
262     stringutils::fieldPrint(ss, fields, args...);
263     ss << "}";
264     return { result, ss.str() };
265 }
266 
AudioAnalytics(const std::shared_ptr<StatsdLog> & statsdLog)267 AudioAnalytics::AudioAnalytics(const std::shared_ptr<StatsdLog>& statsdLog)
268     : mDeliverStatistics(property_get_bool(PROP_AUDIO_ANALYTICS_CLOUD_ENABLED, true))
269     , mStatsdLog(statsdLog)
270     , mAudioPowerUsage(this, statsdLog)
271 {
272     SetMinimumLogSeverity(android::base::DEBUG); // for LOG().
273     ALOGD("%s", __func__);
274 
275     // Add action to save AnalyticsState if audioserver is restarted.
276     // This triggers on an item of "audio.flinger"
277     // with a property "event" set to "AudioFlinger" (the constructor).
278     mActions.addAction(
279         AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
280         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR),
281         std::make_shared<AnalyticsActions::Function>(
282             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
283                 ALOGW("(key=%s) Audioflinger constructor event detected", item->getKey().c_str());
284                 mPreviousAnalyticsState.set(std::make_shared<AnalyticsState>(
285                         *mAnalyticsState.get()));
286                 // Note: get returns shared_ptr temp, whose lifetime is extended
287                 // to end of full expression.
288                 mAnalyticsState->clear();  // TODO: filter the analytics state.
289                 // Perhaps report this.
290 
291                 // Set up a timer to expire the previous audio state to save space.
292                 // Use the transaction log size as a cookie to see if it is the
293                 // same as before.  A benign race is possible where a state is cleared early.
294                 const size_t size = mPreviousAnalyticsState->transactionLog().size();
295                 mTimedAction.postIn(
296                         std::chrono::seconds(PREVIOUS_STATE_EXPIRE_SEC), [this, size](){
297                     if (mPreviousAnalyticsState->transactionLog().size() == size) {
298                         ALOGD("expiring previous audio state after %d seconds.",
299                                 PREVIOUS_STATE_EXPIRE_SEC);
300                         mPreviousAnalyticsState->clear();  // removes data from the state.
301                     }
302                 });
303             }));
304 
305     // Handle legacy aaudio playback stream statistics
306     mActions.addAction(
307         AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
308         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
309         std::make_shared<AnalyticsActions::Function>(
310             [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
311                 mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_LEGACY);
312             }));
313 
314     // Handle legacy aaudio capture stream statistics
315     mActions.addAction(
316         AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
317         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
318         std::make_shared<AnalyticsActions::Function>(
319             [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
320                 mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_LEGACY);
321             }));
322 
323     // Handle mmap aaudio stream statistics
324     mActions.addAction(
325         AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM "*." AMEDIAMETRICS_PROP_EVENT,
326         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAAUDIOSTREAM),
327         std::make_shared<AnalyticsActions::Function>(
328             [this](const std::shared_ptr<const android::mediametrics::Item> &item) {
329                 mAAudioStreamInfo.endAAudioStream(item, AAudioStreamInfo::CALLER_PATH_MMAP);
330             }));
331 
332     // Handle device use record statistics
333     mActions.addAction(
334         AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
335         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
336         std::make_shared<AnalyticsActions::Function>(
337             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
338                 mDeviceUse.endAudioIntervalGroup(item, DeviceUse::RECORD);
339             }));
340 
341     // Handle device use thread statistics
342     mActions.addAction(
343         AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
344         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
345         std::make_shared<AnalyticsActions::Function>(
346             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
347                 mDeviceUse.endAudioIntervalGroup(item, DeviceUse::THREAD);
348             }));
349 
350     // Handle device use track statistics
351     mActions.addAction(
352         AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
353         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
354         std::make_shared<AnalyticsActions::Function>(
355             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
356                 mDeviceUse.endAudioIntervalGroup(item, DeviceUse::TRACK);
357             }));
358 
359 
360     // Handle device connection statistics
361 
362     // We track connections (not disconnections) for the time to connect.
363     // TODO: consider BT requests in their A2dp service
364     // AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
365     // AudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
366     // AudioDeviceBroker.postA2dpActiveDeviceChange
367     mActions.addAction(
368         "audio.device.a2dp.state",
369         "connected",
370         std::make_shared<AnalyticsActions::Function>(
371             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
372                 mDeviceConnection.a2dpConnected(item);
373             }));
374     // If audio is active, we expect to see a createAudioPatch after the device is connected.
375     mActions.addAction(
376         AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
377         std::string("createAudioPatch"),
378         std::make_shared<AnalyticsActions::Function>(
379             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
380                 mDeviceConnection.createPatch(item);
381             }));
382 
383     // Called from BT service
384     mActions.addAction(
385         AMEDIAMETRICS_KEY_PREFIX_AUDIO_DEVICE
386         "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent"
387         "." AMEDIAMETRICS_PROP_STATE,
388         "connected",
389         std::make_shared<AnalyticsActions::Function>(
390             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
391                 mDeviceConnection.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(item);
392             }));
393 
394     // Handle power usage
395     mActions.addAction(
396         AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
397         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
398         std::make_shared<AnalyticsActions::Function>(
399             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
400                 mAudioPowerUsage.checkTrackRecord(item, true /* isTrack */);
401             }));
402 
403     mActions.addAction(
404         AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
405         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
406         std::make_shared<AnalyticsActions::Function>(
407             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
408                 mAudioPowerUsage.checkTrackRecord(item, false /* isTrack */);
409             }));
410 
411     mActions.addAction(
412         AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
413         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE),
414         std::make_shared<AnalyticsActions::Function>(
415             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
416                 // ALOGD("(key=%s) Audioflinger setMode", item->getKey().c_str());
417                 mAudioPowerUsage.checkMode(item);
418             }));
419 
420     mActions.addAction(
421         AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
422         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME),
423         std::make_shared<AnalyticsActions::Function>(
424             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
425                 // ALOGD("(key=%s) Audioflinger setVoiceVolume", item->getKey().c_str());
426                 mAudioPowerUsage.checkVoiceVolume(item);
427             }));
428 
429     mActions.addAction(
430         AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
431         std::string("createAudioPatch"),
432         std::make_shared<AnalyticsActions::Function>(
433             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
434                 mAudioPowerUsage.checkCreatePatch(item);
435             }));
436 }
437 
~AudioAnalytics()438 AudioAnalytics::~AudioAnalytics()
439 {
440     ALOGD("%s", __func__);
441     mTimedAction.quit(); // ensure no deferred access during destructor.
442 }
443 
submit(const std::shared_ptr<const mediametrics::Item> & item,bool isTrusted)444 status_t AudioAnalytics::submit(
445         const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted)
446 {
447     if (!startsWith(item->getKey(), AMEDIAMETRICS_KEY_PREFIX_AUDIO)) return BAD_VALUE;
448     status_t status = mAnalyticsState->submit(item, isTrusted);
449 
450     // Status is selectively authenticated.
451     processStatus(item);
452 
453     if (status != NO_ERROR) return status;  // may not be permitted.
454 
455     // Only if the item was successfully submitted (permission)
456     // do we check triggered actions.
457     processActions(item);
458     return NO_ERROR;
459 }
460 
dump(int32_t lines,int64_t sinceNs,const char * prefix) const461 std::pair<std::string, int32_t> AudioAnalytics::dump(
462         int32_t lines, int64_t sinceNs, const char *prefix) const
463 {
464     std::stringstream ss;
465     int32_t ll = lines;
466 
467     if (ll > 0) {
468         auto [s, l] = mAnalyticsState->dump(ll, sinceNs, prefix);
469         ss << s;
470         ll -= l;
471     }
472     if (ll > 0) {
473         ss << "Prior audioserver state:\n";
474         --ll;
475     }
476     if (ll > 0) {
477         auto [s, l] = mPreviousAnalyticsState->dump(ll, sinceNs, prefix);
478         ss << s;
479         ll -= l;
480     }
481 
482     if (ll > 0 && prefix == nullptr) {
483         auto [s, l] = mAudioPowerUsage.dump(ll);
484         ss << s;
485         ll -= l;
486     }
487 
488     return { ss.str(), lines - ll };
489 }
490 
processActions(const std::shared_ptr<const mediametrics::Item> & item)491 void AudioAnalytics::processActions(const std::shared_ptr<const mediametrics::Item>& item)
492 {
493     auto actions = mActions.getActionsForItem(item); // internally locked.
494     // Execute actions with no lock held.
495     for (const auto& action : actions) {
496         (*action)(item);
497     }
498 }
499 
processStatus(const std::shared_ptr<const mediametrics::Item> & item)500 void AudioAnalytics::processStatus(const std::shared_ptr<const mediametrics::Item>& item)
501 {
502     int32_t status;
503     if (!item->get(AMEDIAMETRICS_PROP_STATUS, &status)) return;
504 
505     // Any record with a status will automatically be added to a heat map.
506     // Standard information.
507     const auto key = item->getKey();
508     const auto uid = item->getUid();
509 
510     // from audio.track.10 ->  prefix = audio.track, suffix = 10
511     // from audio.track.error -> prefix = audio.track, suffix = error
512     const auto [prefixKey, suffixKey] = stringutils::splitPrefixKey(key);
513 
514     std::string message;
515     item->get(AMEDIAMETRICS_PROP_STATUSMESSAGE, &message); // optional
516 
517     int32_t subCode = 0; // not used
518     (void)item->get(AMEDIAMETRICS_PROP_STATUSSUBCODE, &subCode); // optional
519 
520     std::string eventStr; // optional
521     item->get(AMEDIAMETRICS_PROP_EVENT, &eventStr);
522 
523     const std::string statusString = extendedStatusToStatusString(status);
524 
525     // Add to the heat map - we automatically track every item's status to see
526     // the types of errors and the frequency of errors.
527     mHeatMap.add(prefixKey, suffixKey, eventStr, statusString, uid, message, subCode);
528 }
529 
530 // HELPER METHODS
531 
getThreadFromTrack(const std::string & track) const532 std::string AudioAnalytics::getThreadFromTrack(const std::string& track) const
533 {
534     int32_t threadId_int32{};
535     if (mAnalyticsState->timeMachine().get(
536             track, AMEDIAMETRICS_PROP_THREADID, &threadId_int32) != NO_ERROR) {
537         return {};
538     }
539     return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(threadId_int32);
540 }
541 
542 // DeviceUse helper class.
endAudioIntervalGroup(const std::shared_ptr<const android::mediametrics::Item> & item,ItemType itemType) const543 void AudioAnalytics::DeviceUse::endAudioIntervalGroup(
544        const std::shared_ptr<const android::mediametrics::Item> &item, ItemType itemType) const {
545     const std::string& key = item->getKey();
546     const std::string id = key.substr(
547             (itemType == THREAD ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD)
548             : itemType == TRACK ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
549             : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD))
550              - 1);
551     // deliver statistics
552     int64_t deviceTimeNs = 0;
553     mAudioAnalytics.mAnalyticsState->timeMachine().get(
554             key, AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs);
555     std::string encoding;
556     mAudioAnalytics.mAnalyticsState->timeMachine().get(
557             key, AMEDIAMETRICS_PROP_ENCODING, &encoding);
558     int32_t frameCount = 0;
559     mAudioAnalytics.mAnalyticsState->timeMachine().get(
560             key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
561     std::string inputDevicePairs;
562     mAudioAnalytics.mAnalyticsState->timeMachine().get(
563             key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevicePairs);
564     int32_t intervalCount = 0;
565     mAudioAnalytics.mAnalyticsState->timeMachine().get(
566             key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
567     std::string outputDevicePairs;
568     mAudioAnalytics.mAnalyticsState->timeMachine().get(
569             key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevicePairs);
570     int32_t sampleRate = 0;
571     mAudioAnalytics.mAnalyticsState->timeMachine().get(
572             key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
573     std::string flags;
574     mAudioAnalytics.mAnalyticsState->timeMachine().get(
575             key, AMEDIAMETRICS_PROP_FLAGS, &flags);
576 
577     // We may have several devices.
578     // Accumulate the bit flags for input and output devices.
579     std::stringstream oss;
580     long_enum_type_t outputDeviceBits{};
581     {   // compute outputDevices
582         const auto devaddrvec = stringutils::getDeviceAddressPairs(outputDevicePairs);
583         for (const auto& [device, addr] : devaddrvec) {
584             if (oss.tellp() > 0) oss << "|";  // delimit devices with '|'.
585             oss << device;
586             outputDeviceBits += types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(device);
587         }
588     }
589     const std::string outputDevices = oss.str();
590 
591     std::stringstream iss;
592     long_enum_type_t inputDeviceBits{};
593     {   // compute inputDevices
594         const auto devaddrvec = stringutils::getDeviceAddressPairs(inputDevicePairs);
595         for (const auto& [device, addr] : devaddrvec) {
596             if (iss.tellp() > 0) iss << "|";  // delimit devices with '|'.
597             iss << device;
598             inputDeviceBits += types::lookup<types::INPUT_DEVICE, long_enum_type_t>(device);
599         }
600     }
601     const std::string inputDevices = iss.str();
602 
603     // Get connected device name if from bluetooth.
604     bool isBluetooth = false;
605 
606     std::string inputDeviceNames;  // not filled currently.
607     std::string outputDeviceNames;
608     if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
609         isBluetooth = true;
610         outputDeviceNames = SUPPRESSED;
611 #if 0   // TODO(b/161554630) sanitize name
612         mAudioAnalytics.mAnalyticsState->timeMachine().get(
613             "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &outputDeviceNames);
614         // Remove | if present
615         stringutils::replace(outputDeviceNames, "|", '?');
616         if (outputDeviceNames.size() > STATSD_DEVICE_NAME_MAX_LENGTH) {
617             outputDeviceNames.resize(STATSD_DEVICE_NAME_MAX_LENGTH); // truncate
618         }
619 #endif
620     }
621 
622     switch (itemType) {
623     case RECORD: {
624         std::string callerName;
625         const bool clientCalled = mAudioAnalytics.mAnalyticsState->timeMachine().get(
626                 key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName) == OK;
627 
628         std::string packageName;
629         int64_t versionCode = 0;
630         int32_t uid = -1;
631         mAudioAnalytics.mAnalyticsState->timeMachine().get(
632                 key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
633         if (uid != -1) {
634             std::tie(packageName, versionCode) =
635                     MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
636         }
637 
638         int32_t selectedDeviceId = 0;
639         mAudioAnalytics.mAnalyticsState->timeMachine().get(
640                 key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
641         std::string source;
642         mAudioAnalytics.mAnalyticsState->timeMachine().get(
643                 key, AMEDIAMETRICS_PROP_SOURCE, &source);
644         // Android S
645         std::string logSessionId;
646         mAudioAnalytics.mAnalyticsState->timeMachine().get(
647                 key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
648 
649         const auto callerNameForStats =
650                 types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
651         const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
652         const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags);
653         const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source);
654         // Android S
655         const auto logSessionIdForStats = ValidateId::get()->validateId(logSessionId);
656 
657         LOG(LOG_LEVEL) << "key:" << key
658               << " id:" << id
659               << " inputDevices:" << inputDevices << "(" << inputDeviceBits
660               << ") inputDeviceNames:" << inputDeviceNames
661               << " deviceTimeNs:" << deviceTimeNs
662               << " encoding:" << encoding << "(" << encodingForStats
663               << ") frameCount:" << frameCount
664               << " intervalCount:" << intervalCount
665               << " sampleRate:" << sampleRate
666               << " flags:" << flags << "(" << flagsForStats
667               << ") packageName:" << packageName
668               << " selectedDeviceId:" << selectedDeviceId
669               << " callerName:" << callerName << "(" << callerNameForStats
670               << ") source:" << source << "(" << sourceForStats
671               << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats
672               << ")";
673         if (clientCalled  // only log if client app called AudioRecord.
674                 && mAudioAnalytics.mDeliverStatistics) {
675             const auto [ result, str ] = sendToStatsd(AudioRecordDeviceUsageFields,
676                     CONDITION(android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED)
677                     , ENUM_EXTRACT(inputDeviceBits)
678                     , inputDeviceNames.c_str()
679                     , deviceTimeNs
680                     , ENUM_EXTRACT(encodingForStats)
681                     , frameCount
682                     , intervalCount
683                     , sampleRate
684                     , ENUM_EXTRACT(flagsForStats)
685 
686                     , packageName.c_str()
687                     , selectedDeviceId
688                     , ENUM_EXTRACT(callerNameForStats)
689                     , ENUM_EXTRACT(sourceForStats)
690                     , logSessionIdForStats.c_str()
691                     );
692             ALOGV("%s: statsd %s", __func__, str.c_str());
693             mAudioAnalytics.mStatsdLog->log(
694                     android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED, str);
695         }
696     } break;
697     case THREAD: {
698         std::string type;
699         mAudioAnalytics.mAnalyticsState->timeMachine().get(
700                 key, AMEDIAMETRICS_PROP_TYPE, &type);
701         int32_t underrun = 0; // zero for record types
702         mAudioAnalytics.mAnalyticsState->timeMachine().get(
703                 key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
704 
705         const bool isInput = types::isInputThreadType(type);
706         const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
707         const auto flagsForStats =
708                 (isInput ? types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags)
709                         : types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags));
710         const auto typeForStats = types::lookup<types::THREAD_TYPE, short_enum_type_t>(type);
711 
712         LOG(LOG_LEVEL) << "key:" << key
713               << " id:" << id
714               << " inputDevices:" << inputDevices << "(" << inputDeviceBits
715               << ") outputDevices:" << outputDevices << "(" << outputDeviceBits
716               << ") inputDeviceNames:" << inputDeviceNames
717               << " outputDeviceNames:" << outputDeviceNames
718               << " deviceTimeNs:" << deviceTimeNs
719               << " encoding:" << encoding << "(" << encodingForStats
720               << ") frameCount:" << frameCount
721               << " intervalCount:" << intervalCount
722               << " sampleRate:" << sampleRate
723               << " underrun:" << underrun
724               << " flags:" << flags << "(" << flagsForStats
725               << ") type:" << type << "(" << typeForStats
726               << ")";
727         if (mAudioAnalytics.mDeliverStatistics) {
728             const auto [ result, str ] = sendToStatsd(AudioThreadDeviceUsageFields,
729                 CONDITION(android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED)
730                 , isInput ? ENUM_EXTRACT(inputDeviceBits) : ENUM_EXTRACT(outputDeviceBits)
731                 , isInput ? inputDeviceNames.c_str() : outputDeviceNames.c_str()
732                 , deviceTimeNs
733                 , ENUM_EXTRACT(encodingForStats)
734                 , frameCount
735                 , intervalCount
736                 , sampleRate
737                 , ENUM_EXTRACT(flagsForStats)
738                 , underrun
739                 , ENUM_EXTRACT(typeForStats)
740             );
741             ALOGV("%s: statsd %s", __func__, str.c_str());
742             mAudioAnalytics.mStatsdLog->log(
743                     android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED, str);
744         }
745     } break;
746     case TRACK: {
747         std::string callerName;
748         const bool clientCalled = mAudioAnalytics.mAnalyticsState->timeMachine().get(
749                 key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName) == OK;
750 
751         std::string contentType;
752         mAudioAnalytics.mAnalyticsState->timeMachine().get(
753                 key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentType);
754         double deviceLatencyMs = 0.;
755         mAudioAnalytics.mAnalyticsState->timeMachine().get(
756                 key, AMEDIAMETRICS_PROP_DEVICELATENCYMS, &deviceLatencyMs);
757         double deviceStartupMs = 0.;
758         mAudioAnalytics.mAnalyticsState->timeMachine().get(
759                 key, AMEDIAMETRICS_PROP_DEVICESTARTUPMS, &deviceStartupMs);
760         double deviceVolume = 0.;
761         mAudioAnalytics.mAnalyticsState->timeMachine().get(
762                 key, AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume);
763         std::string packageName;
764         int64_t versionCode = 0;
765         int32_t uid = -1;
766         mAudioAnalytics.mAnalyticsState->timeMachine().get(
767                 key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
768         if (uid != -1) {
769             std::tie(packageName, versionCode) =
770                     MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
771         }
772         double playbackPitch = 0.;
773         mAudioAnalytics.mAnalyticsState->timeMachine().get(
774                 key, AMEDIAMETRICS_PROP_PLAYBACK_PITCH, &playbackPitch);
775         double playbackSpeed = 0.;
776         mAudioAnalytics.mAnalyticsState->timeMachine().get(
777                 key, AMEDIAMETRICS_PROP_PLAYBACK_SPEED, &playbackSpeed);
778         int32_t selectedDeviceId = 0;
779         mAudioAnalytics.mAnalyticsState->timeMachine().get(
780                 key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
781         std::string streamType;
782         mAudioAnalytics.mAnalyticsState->timeMachine().get(
783                 key, AMEDIAMETRICS_PROP_STREAMTYPE, &streamType);
784         std::string traits;
785         mAudioAnalytics.mAnalyticsState->timeMachine().get(
786                 key, AMEDIAMETRICS_PROP_TRAITS, &traits);
787         int32_t underrun = 0;
788         mAudioAnalytics.mAnalyticsState->timeMachine().get(
789                 key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
790         std::string usage;
791         mAudioAnalytics.mAnalyticsState->timeMachine().get(
792                 key, AMEDIAMETRICS_PROP_USAGE, &usage);
793         // Android S
794         std::string logSessionId;
795         mAudioAnalytics.mAnalyticsState->timeMachine().get(
796                 key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
797 
798         const auto callerNameForStats =
799                 types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
800         const auto contentTypeForStats =
801                 types::lookup<types::CONTENT_TYPE, short_enum_type_t>(contentType);
802         const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
803         const auto flagsForStats = types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags);
804         const auto streamTypeForStats =
805                 types::lookup<types::STREAM_TYPE, short_enum_type_t>(streamType);
806         const auto traitsForStats =
807                  types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits);
808         const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
809         // Android S
810         const auto logSessionIdForStats = ValidateId::get()->validateId(logSessionId);
811 
812         LOG(LOG_LEVEL) << "key:" << key
813               << " id:" << id
814               << " outputDevices:" << outputDevices << "(" << outputDeviceBits
815               << ") outputDeviceNames:" << outputDeviceNames
816               << " deviceTimeNs:" << deviceTimeNs
817               << " encoding:" << encoding << "(" << encodingForStats
818               << ") frameCount:" << frameCount
819               << " intervalCount:" << intervalCount
820               << " sampleRate:" << sampleRate
821               << " underrun:" << underrun
822               << " flags:" << flags << "(" << flagsForStats
823               << ") callerName:" << callerName << "(" << callerNameForStats
824               << ") contentType:" << contentType << "(" << contentTypeForStats
825               << ") deviceLatencyMs:" << deviceLatencyMs
826               << " deviceStartupMs:" << deviceStartupMs
827               << " deviceVolume:" << deviceVolume
828               << " packageName:" << packageName
829               << " playbackPitch:" << playbackPitch
830               << " playbackSpeed:" << playbackSpeed
831               << " selectedDeviceId:" << selectedDeviceId
832               << " streamType:" << streamType << "(" << streamTypeForStats
833               << ") traits:" << traits << "(" << traitsForStats
834               << ") usage:" << usage << "(" << usageForStats
835               << ") logSessionId:" << logSessionId << "(" << logSessionIdForStats
836               << ")";
837         if (clientCalled // only log if client app called AudioTracks
838                 && mAudioAnalytics.mDeliverStatistics) {
839             const auto [ result, str ] = sendToStatsd(AudioTrackDeviceUsageFields,
840                     CONDITION(android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED)
841                     , ENUM_EXTRACT(outputDeviceBits)
842                     , outputDeviceNames.c_str()
843                     , deviceTimeNs
844                     , ENUM_EXTRACT(encodingForStats)
845                     , frameCount
846                     , intervalCount
847                     , sampleRate
848                     , ENUM_EXTRACT(flagsForStats)
849                     , underrun
850                     , packageName.c_str()
851                     , (float)deviceLatencyMs
852                     , (float)deviceStartupMs
853                     , (float)deviceVolume
854                     , selectedDeviceId
855                     , ENUM_EXTRACT(streamTypeForStats)
856                     , ENUM_EXTRACT(usageForStats)
857                     , ENUM_EXTRACT(contentTypeForStats)
858                     , ENUM_EXTRACT(callerNameForStats)
859                     , ENUM_EXTRACT(traitsForStats)
860                     , logSessionIdForStats.c_str()
861                     );
862             ALOGV("%s: statsd %s", __func__, str.c_str());
863             mAudioAnalytics.mStatsdLog->log(
864                     android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED, str);
865         }
866         } break;
867     }
868 
869     // Report this as needed.
870     if (isBluetooth) {
871         // report this for Bluetooth
872     }
873 }
874 
875 // DeviceConnection helper class.
a2dpConnected(const std::shared_ptr<const android::mediametrics::Item> & item)876 void AudioAnalytics::DeviceConnection::a2dpConnected(
877        const std::shared_ptr<const android::mediametrics::Item> &item) {
878     const std::string& key = item->getKey();
879     const int64_t atNs = item->getTimestamp();
880     {
881         std::lock_guard l(mLock);
882         mA2dpConnectionServiceNs = atNs;
883         ++mA2dpConnectionServices;
884 
885         if (mA2dpConnectionRequestNs == 0) {
886             mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
887         }
888         // This sets the time we were connected.  Now we look for the delta in the future.
889     }
890     std::string name;
891     item->get(AMEDIAMETRICS_PROP_NAME, &name);
892     ALOGD("(key=%s) a2dp connected device:%s atNs:%lld",
893             key.c_str(), name.c_str(), (long long)atNs);
894 }
895 
createPatch(const std::shared_ptr<const android::mediametrics::Item> & item)896 void AudioAnalytics::DeviceConnection::createPatch(
897        const std::shared_ptr<const android::mediametrics::Item> &item) {
898     std::lock_guard l(mLock);
899     if (mA2dpConnectionServiceNs == 0) return; // patch unrelated to us.
900     const std::string& key = item->getKey();
901     std::string outputDevices;
902     item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
903     if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH_A2DP") != std::string::npos) {
904         // TODO compare address
905         int64_t timeDiffNs = item->getTimestamp();
906         if (mA2dpConnectionRequestNs == 0) {
907             ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
908             timeDiffNs -= mA2dpConnectionServiceNs;
909         } else {
910             timeDiffNs -= mA2dpConnectionRequestNs;
911         }
912 
913         mA2dpConnectionRequestNs = 0;
914         mA2dpConnectionServiceNs = 0;
915         ++mA2dpConnectionSuccesses;
916 
917         const auto connectionTimeMs = float((double)timeDiffNs * 1e-6);
918 
919         const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
920                 "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
921 
922         LOG(LOG_LEVEL) << "key:" << key
923                 << " A2DP SUCCESS"
924                 << " outputDevices:" << outputDeviceBits
925                 << " deviceName:" << mA2dpDeviceName
926                 << " connectionTimeMs:" <<  connectionTimeMs;
927         if (mAudioAnalytics.mDeliverStatistics) {
928             const long_enum_type_t inputDeviceBits{};
929 
930             const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
931                     CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
932                     , ENUM_EXTRACT(inputDeviceBits)
933                     , ENUM_EXTRACT(outputDeviceBits)
934                     , mA2dpDeviceName.c_str()
935                     , types::DEVICE_CONNECTION_RESULT_SUCCESS
936                     , connectionTimeMs
937                     , /* connection_count */ 1
938                     );
939             ALOGV("%s: statsd %s", __func__, str.c_str());
940             mAudioAnalytics.mStatsdLog->log(
941                     android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
942         }
943     }
944 }
945 
946 // Called through AudioManager when the BT service wants to enable
postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(const std::shared_ptr<const android::mediametrics::Item> & item)947 void AudioAnalytics::DeviceConnection::postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
948         const std::shared_ptr<const android::mediametrics::Item> &item) {
949     const int64_t atNs = item->getTimestamp();
950     const std::string& key = item->getKey();
951     std::string state;
952     item->get(AMEDIAMETRICS_PROP_STATE, &state);
953     if (state != "connected") return;
954 
955     std::string name;
956     item->get(AMEDIAMETRICS_PROP_NAME, &name);
957     {
958         std::lock_guard l(mLock);
959         mA2dpConnectionRequestNs = atNs;
960         ++mA2dpConnectionRequests;
961         mA2dpDeviceName = SUPPRESSED; // TODO(b/161554630) sanitize name
962     }
963     ALOGD("(key=%s) a2dp connection name:%s request atNs:%lld",
964             key.c_str(), name.c_str(), (long long)atNs);
965     // TODO: attempt to cancel a timed event, rather than let it expire.
966     mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
967 }
968 
expire()969 void AudioAnalytics::DeviceConnection::expire() {
970     std::lock_guard l(mLock);
971     if (mA2dpConnectionRequestNs == 0) return; // ignore (this was an internal connection).
972 
973     const long_enum_type_t inputDeviceBits{};
974     const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
975             "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
976 
977     if (mA2dpConnectionServiceNs == 0) {
978         ++mA2dpConnectionJavaServiceCancels;  // service did not connect to A2DP
979 
980         LOG(LOG_LEVEL) << "A2DP CANCEL"
981                 << " outputDevices:" << outputDeviceBits
982                 << " deviceName:" << mA2dpDeviceName;
983         if (mAudioAnalytics.mDeliverStatistics) {
984             const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
985                     CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
986                     , ENUM_EXTRACT(inputDeviceBits)
987                     , ENUM_EXTRACT(outputDeviceBits)
988                     , mA2dpDeviceName.c_str()
989                     , types::DEVICE_CONNECTION_RESULT_JAVA_SERVICE_CANCEL
990                     , /* connection_time_ms */ 0.f
991                     , /* connection_count */ 1
992                     );
993             ALOGV("%s: statsd %s", __func__, str.c_str());
994             mAudioAnalytics.mStatsdLog->log(
995                     android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
996         }
997         return;
998     }
999 
1000     // AudioFlinger didn't play - an expiration may occur because there is no audio playing.
1001     // Should we check elsewhere?
1002     // TODO: disambiguate this case.
1003     mA2dpConnectionRequestNs = 0;
1004     mA2dpConnectionServiceNs = 0;
1005     ++mA2dpConnectionUnknowns;  // connection result unknown
1006 
1007     LOG(LOG_LEVEL) << "A2DP UNKNOWN"
1008             << " outputDevices:" << outputDeviceBits
1009             << " deviceName:" << mA2dpDeviceName;
1010     if (mAudioAnalytics.mDeliverStatistics) {
1011         const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
1012                 CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
1013                 , ENUM_EXTRACT(inputDeviceBits)
1014                 , ENUM_EXTRACT(outputDeviceBits)
1015                 , mA2dpDeviceName.c_str()
1016                 , types::DEVICE_CONNECTION_RESULT_UNKNOWN
1017                 , /* connection_time_ms */ 0.f
1018                 , /* connection_count */ 1
1019                 );
1020         ALOGV("%s: statsd %s", __func__, str.c_str());
1021         mAudioAnalytics.mStatsdLog->log(
1022                 android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED, str);
1023     }
1024 }
1025 
endAAudioStream(const std::shared_ptr<const android::mediametrics::Item> & item,CallerPath path) const1026 void AudioAnalytics::AAudioStreamInfo::endAAudioStream(
1027         const std::shared_ptr<const android::mediametrics::Item> &item, CallerPath path) const {
1028     const std::string& key = item->getKey();
1029 
1030     std::string directionStr;
1031     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1032             key, AMEDIAMETRICS_PROP_DIRECTION, &directionStr);
1033     const auto direction = types::lookup<types::AAUDIO_DIRECTION, int32_t>(directionStr);
1034 
1035     int32_t framesPerBurst = -1;
1036     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1037             key, AMEDIAMETRICS_PROP_BURSTFRAMES, &framesPerBurst);
1038 
1039     int32_t bufferSizeInFrames = -1;
1040     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1041             key, AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, &bufferSizeInFrames);
1042 
1043     int32_t bufferCapacityInFrames = -1;
1044     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1045             key, AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, &bufferCapacityInFrames);
1046 
1047     int32_t channelCount = -1;
1048     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1049             key, AMEDIAMETRICS_PROP_CHANNELCOUNT, &channelCount);
1050     if (channelCount == -1) {
1051         // Try to get channel count from channel mask. From the legacy path,
1052         // only channel mask are logged.
1053         int32_t channelMask = 0;
1054         mAudioAnalytics.mAnalyticsState->timeMachine().get(
1055                 key, AMEDIAMETRICS_PROP_CHANNELMASK, &channelMask);
1056         if (channelMask != 0) {
1057             switch (direction) {
1058                 case 1: // Output, keep sync with AudioTypes#getAAudioDirection()
1059                     channelCount = audio_channel_count_from_out_mask(channelMask);
1060                     break;
1061                 case 2: // Input, keep sync with AudioTypes#getAAudioDirection()
1062                     channelCount = audio_channel_count_from_in_mask(channelMask);
1063                     break;
1064                 default:
1065                     ALOGW("Invalid direction %d", direction);
1066             }
1067         }
1068     }
1069 
1070     int64_t totalFramesTransferred = -1;
1071     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1072             key, AMEDIAMETRICS_PROP_FRAMESTRANSFERRED, &totalFramesTransferred);
1073 
1074     std::string perfModeRequestedStr;
1075     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1076             key, AMEDIAMETRICS_PROP_PERFORMANCEMODE, &perfModeRequestedStr);
1077     const auto perfModeRequested =
1078             types::lookup<types::AAUDIO_PERFORMANCE_MODE, int32_t>(perfModeRequestedStr);
1079 
1080     std::string perfModeActualStr;
1081     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1082             key, AMEDIAMETRICS_PROP_PERFORMANCEMODEACTUAL, &perfModeActualStr);
1083     const auto perfModeActual =
1084             types::lookup<types::AAUDIO_PERFORMANCE_MODE, int32_t>(perfModeActualStr);
1085 
1086     std::string sharingModeActualStr;
1087     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1088             key, AMEDIAMETRICS_PROP_SHARINGMODEACTUAL, &sharingModeActualStr);
1089     const auto sharingModeActual =
1090             types::lookup<types::AAUDIO_SHARING_MODE, int32_t>(sharingModeActualStr);
1091 
1092     int32_t xrunCount = -1;
1093     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1094             key, AMEDIAMETRICS_PROP_UNDERRUN, &xrunCount);
1095 
1096     std::string serializedDeviceTypes;
1097     // TODO: only routed device id is logged, but no device type
1098 
1099     std::string formatAppStr;
1100     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1101             key, AMEDIAMETRICS_PROP_ENCODINGCLIENT, &formatAppStr);
1102     const auto formatApp = types::lookup<types::ENCODING, int32_t>(formatAppStr);
1103 
1104     std::string formatDeviceStr;
1105     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1106             key, AMEDIAMETRICS_PROP_ENCODING, &formatDeviceStr);
1107     const auto formatDevice = types::lookup<types::ENCODING, int32_t>(formatDeviceStr);
1108 
1109     std::string logSessionId;
1110     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1111             key, AMEDIAMETRICS_PROP_LOGSESSIONID, &logSessionId);
1112 
1113     int32_t sampleRate = 0;
1114     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1115             key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
1116 
1117     std::string contentTypeStr;
1118     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1119             key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentTypeStr);
1120     const auto contentType = types::lookup<types::CONTENT_TYPE, int32_t>(contentTypeStr);
1121 
1122     std::string sharingModeRequestedStr;
1123     mAudioAnalytics.mAnalyticsState->timeMachine().get(
1124             key, AMEDIAMETRICS_PROP_SHARINGMODE, &sharingModeRequestedStr);
1125     const auto sharingModeRequested =
1126             types::lookup<types::AAUDIO_SHARING_MODE, int32_t>(sharingModeRequestedStr);
1127 
1128     LOG(LOG_LEVEL) << "key:" << key
1129             << " path:" << path
1130             << " direction:" << direction << "(" << directionStr << ")"
1131             << " frames_per_burst:" << framesPerBurst
1132             << " buffer_size:" << bufferSizeInFrames
1133             << " buffer_capacity:" << bufferCapacityInFrames
1134             << " channel_count:" << channelCount
1135             << " total_frames_transferred:" << totalFramesTransferred
1136             << " perf_mode_requested:" << perfModeRequested << "(" << perfModeRequestedStr << ")"
1137             << " perf_mode_actual:" << perfModeActual << "(" << perfModeActualStr << ")"
1138             << " sharing:" << sharingModeActual << "(" << sharingModeActualStr << ")"
1139             << " xrun_count:" << xrunCount
1140             << " device_type:" << serializedDeviceTypes
1141             << " format_app:" << formatApp << "(" << formatAppStr << ")"
1142             << " format_device: " << formatDevice << "(" << formatDeviceStr << ")"
1143             << " log_session_id: " << logSessionId
1144             << " sample_rate: " << sampleRate
1145             << " content_type: " << contentType << "(" << contentTypeStr << ")"
1146             << " sharing_requested:" << sharingModeRequested
1147                     << "(" << sharingModeRequestedStr << ")";
1148 
1149     if (mAudioAnalytics.mDeliverStatistics) {
1150         android::util::BytesField bf_serialized(
1151             serializedDeviceTypes.c_str(), serializedDeviceTypes.size());
1152         const auto result = sendToStatsd(
1153                 CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
1154                 , path
1155                 , direction
1156                 , framesPerBurst
1157                 , bufferSizeInFrames
1158                 , bufferCapacityInFrames
1159                 , channelCount
1160                 , totalFramesTransferred
1161                 , perfModeRequested
1162                 , perfModeActual
1163                 , sharingModeActual
1164                 , xrunCount
1165                 , bf_serialized
1166                 , formatApp
1167                 , formatDevice
1168                 , logSessionId.c_str()
1169                 , sampleRate
1170                 , contentType
1171                 , sharingModeRequested
1172                 );
1173         std::stringstream ss;
1174         ss << "result:" << result;
1175         const auto fieldsStr = printFields(AAudioStreamFields,
1176                 CONDITION(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED)
1177                 , path
1178                 , direction
1179                 , framesPerBurst
1180                 , bufferSizeInFrames
1181                 , bufferCapacityInFrames
1182                 , channelCount
1183                 , totalFramesTransferred
1184                 , perfModeRequested
1185                 , perfModeActual
1186                 , sharingModeActual
1187                 , xrunCount
1188                 , serializedDeviceTypes.c_str()
1189                 , formatApp
1190                 , formatDevice
1191                 , logSessionId.c_str()
1192                 , sampleRate
1193                 , contentType
1194                 , sharingModeRequested
1195                 );
1196         ss << " " << fieldsStr;
1197         std::string str = ss.str();
1198         ALOGV("%s: statsd %s", __func__, str.c_str());
1199         mAudioAnalytics.mStatsdLog->log(android::util::MEDIAMETRICS_AAUDIOSTREAM_REPORTED, str);
1200     }
1201 }
1202 
1203 } // namespace android::mediametrics
1204