MediaMetrics: Update and clean proto formatting
Make code compatible with both string
and integer enumeration in the protos.
Test: adb shell dumpsys media.metrics
Test: atest mediametrics_tests
Bug: 149850236
Change-Id: I0a1c21be711f82915f3bb403860b40c695a0587d
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 31ad234..800f099 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -16,20 +16,45 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioAnalytics"
+#include <android-base/logging.h>
#include <utils/Log.h>
#include "AudioAnalytics.h"
-#include "MediaMetricsService.h" // package info
+
#include <audio_utils/clock.h> // clock conversions
+#include <cutils/properties.h>
#include <statslog.h> // statsd
+#include "AudioTypes.h" // string to int conversions
+#include "MediaMetricsService.h" // package info
+#include "StringUtils.h"
+
+#define PROP_AUDIO_ANALYTICS_CLOUD_ENABLED "persist.audio.analytics.cloud.enabled"
+
// Enable for testing of delivery to statsd
-// #define STATSD
+//#define STATSD
+
+// Transmit to statsd in integer or strings
+//#define USE_INT
+
+#ifdef USE_INT
+using short_enum_type_t = int32_t;
+using long_enum_type_t = int64_t;
+#define ENUM_EXTRACT(x) (x)
+#else
+using short_enum_type_t = std::string;
+using long_enum_type_t = std::string;
+#define ENUM_EXTRACT(x) (x).c_str()
+#endif
+
+using android::base::DEBUG;
namespace android::mediametrics {
AudioAnalytics::AudioAnalytics()
+ : mDeliverStatistics(property_get_bool(PROP_AUDIO_ANALYTICS_CLOUD_ENABLED, true))
{
+ SetMinimumLogSeverity(DEBUG); // for LOG().
ALOGD("%s", __func__);
// Add action to save AnalyticsState if audioserver is restarted.
@@ -243,33 +268,47 @@
int32_t frameCount = 0;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
- std::string inputDevices;
+ std::string inputDevicePairs;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
- key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevices);
+ key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevicePairs);
int32_t intervalCount = 0;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
- std::string outputDevices;
+ std::string outputDevicePairs;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
- key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+ key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevicePairs);
int32_t sampleRate = 0;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
std::string flags;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_FLAGS, &flags);
+
// We may have several devices.
- // Strings allow us to mix input and output devices together.
- // TODO: review if we want to separate them.
- std::stringstream ss;
- for (const auto& devicePairs : { outputDevices, inputDevices }) {
- const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(devicePairs);
+ // Accumulate the bit flags for input and output devices.
+ std::stringstream oss;
+ long_enum_type_t outputDeviceBits{};
+ { // compute outputDevices
+ const auto devaddrvec = stringutils::getDeviceAddressPairs(outputDevicePairs);
for (const auto& [device, addr] : devaddrvec) {
- if (ss.tellp() > 0) ss << "|"; // delimit devices with '|'.
- ss << device;
+ if (oss.tellp() > 0) oss << "|"; // delimit devices with '|'.
+ oss << device;
+ outputDeviceBits += types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(device);
}
}
- std::string devices = ss.str();
+ const std::string outputDevices = oss.str();
+
+ std::stringstream iss;
+ long_enum_type_t inputDeviceBits{};
+ { // compute inputDevices
+ const auto devaddrvec = stringutils::getDeviceAddressPairs(inputDevicePairs);
+ for (const auto& [device, addr] : devaddrvec) {
+ if (iss.tellp() > 0) iss << "|"; // delimit devices with '|'.
+ iss << device;
+ inputDeviceBits += types::lookup<types::INPUT_DEVICE, long_enum_type_t>(device);
+ }
+ }
+ const std::string inputDevices = iss.str();
// Get connected device name if from bluetooth.
bool isBluetooth = false;
@@ -278,8 +317,8 @@
isBluetooth = true;
mAudioAnalytics.mAnalyticsState->timeMachine().get(
"audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &deviceNames);
- // We don't check if deviceName is sanitized.
- // TODO: remove reserved chars such as '|' and replace with a char like '_'.
+ // Remove | if present
+ stringutils::replace(deviceNames, "|", '?');
}
switch (itemType) {
@@ -305,37 +344,43 @@
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_SOURCE, &source);
- ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
- "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
- "sampleRate:%d "
- "packageName:%s "
- "selectedDeviceId:%d "
- "callerName:%s source:%s",
- key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
- (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
- sampleRate,
- packageName.c_str(), selectedDeviceId,
- callerName.c_str(), source.c_str());
+ const auto callerNameForStats =
+ types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
+ const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+ const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags);
+ const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source);
+ LOG(DEBUG) << "key:" << key
+ << " id:" << id
+ << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+ << ") deviceNames:" << deviceNames
+ << " deviceTimeNs:" << deviceTimeNs
+ << " encoding:" << encoding << "(" << encodingForStats
+ << ") frameCount:" << frameCount
+ << " intervalCount:" << intervalCount
+ << " sampleRate:" << sampleRate
+ << " flags:" << flags << "(" << flagsForStats
+ << ") packageName:" << packageName
+ << " selectedDeviceId:" << selectedDeviceId
+ << " callerName:" << callerName << "(" << callerNameForStats
+ << ") source:" << source << "(" << sourceForStats << ")";
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , devices.c_str()
+ , ENUM_EXTRACT(inputDeviceBits)
, deviceNames.c_str()
, deviceTimeNs
- , encoding.c_str()
+ , ENUM_EXTRACT(encodingForStats)
, frameCount
, intervalCount
, sampleRate
- , flags.c_str()
+ , ENUM_EXTRACT(flagsForStats)
, packageName.c_str()
, selectedDeviceId
- , callerName.c_str()
- , source.c_str()
+ , ENUM_EXTRACT(callerNameForStats)
+ , ENUM_EXTRACT(sourceForStats)
);
}
#endif
@@ -347,31 +392,43 @@
int32_t underrun = 0; // zero for record types
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
- ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
- "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
- "sampleRate:%d underrun:%d "
- "flags:%s type:%s",
- key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
- (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
- sampleRate, underrun,
- flags.c_str(), type.c_str());
+
+ const bool isInput = types::isInputThreadType(type);
+ const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+ const auto flagsForStats =
+ (isInput ? types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags)
+ : types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags));
+ const auto typeForStats = types::lookup<types::THREAD_TYPE, short_enum_type_t>(type);
+
+ LOG(DEBUG) << "key:" << key
+ << " id:" << id
+ << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+ << ") outputDevices:" << outputDevices << "(" << outputDeviceBits
+ << ") deviceNames:" << deviceNames
+ << " deviceTimeNs:" << deviceTimeNs
+ << " encoding:" << encoding << "(" << encodingForStats
+ << ") frameCount:" << frameCount
+ << " intervalCount:" << intervalCount
+ << " sampleRate:" << sampleRate
+ << " underrun:" << underrun
+ << " flags:" << flags << "(" << flagsForStats
+ << ") type:" << type << "(" << typeForStats
+ << ")";
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , devices.c_str()
+ , ENUM_EXTRACT(inputDeviceBits)
+ , ENUM_EXTRACT(outputDeviceBits)
, deviceNames.c_str()
, deviceTimeNs
- , encoding.c_str()
+ , ENUM_EXTRACT(encodingForStats)
, frameCount
, intervalCount
, sampleRate
- , flags.c_str()
-
+ , ENUM_EXTRACT(flagsForStats)
, underrun
- , type.c_str()
+ , ENUM_EXTRACT(typeForStats)
);
}
#endif
@@ -420,34 +477,51 @@
mAudioAnalytics.mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_USAGE, &usage);
- ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
- "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
- "sampleRate:%d underrun:%d "
- "callerName:%s contentType:%s "
- "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf "
- "packageName:%s playbackPitch:%lf playbackSpeed:%lf "
- "selectedDeviceId:%d streamType:%s usage:%s",
- key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
- (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
- sampleRate, underrun,
- callerName.c_str(), contentType.c_str(),
- deviceLatencyMs, deviceStartupMs, deviceVolume,
- packageName.c_str(), playbackPitch, playbackSpeed,
- selectedDeviceId, streamType.c_str(), usage.c_str());
+ const auto callerNameForStats =
+ types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
+ const auto contentTypeForStats =
+ types::lookup<types::CONTENT_TYPE, short_enum_type_t>(contentType);
+ const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+ const auto flagsForStats = types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags);
+ const auto streamTypeForStats =
+ types::lookup<types::STREAM_TYPE, short_enum_type_t>(streamType);
+ const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
+
+ LOG(DEBUG) << "key:" << key
+ << " id:" << id
+ << " outputDevices:" << outputDevices << "(" << outputDeviceBits
+ << ") deviceNames:" << deviceNames
+ << " deviceTimeNs:" << deviceTimeNs
+ << " encoding:" << encoding << "(" << encodingForStats
+ << ") frameCount:" << frameCount
+ << " intervalCount:" << intervalCount
+ << " sampleRate:" << sampleRate
+ << " underrun:" << underrun
+ << " flags:" << flags << "(" << flagsForStats
+ << ") callerName:" << callerName << "(" << callerNameForStats
+ << ") contentType:" << contentType << "(" << contentTypeForStats
+ << ") deviceLatencyMs:" << deviceLatencyMs
+ << " deviceStartupMs:" << deviceStartupMs
+ << " deviceVolume:" << deviceVolume
+ << " packageName:" << packageName
+ << " playbackPitch:" << playbackPitch
+ << " playbackSpeed:" << playbackSpeed
+ << " selectedDeviceId:" << selectedDeviceId
+ << " streamType:" << streamType << "(" << streamTypeForStats
+ << ") usage:" << usage << "(" << usageForStats
+ << ")";
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , devices.c_str()
+ , ENUM_EXTRACT(outputDeviceBits)
, deviceNames.c_str()
, deviceTimeNs
- , encoding.c_str()
+ , ENUM_EXTRACT(encodingForStats)
, frameCount
, intervalCount
, sampleRate
- , flags.c_str()
+ , ENUM_EXTRACT(flagsForStats)
, underrun
, packageName.c_str()
@@ -455,10 +529,10 @@
, (float)deviceStartupMs
, (float)deviceVolume
, selectedDeviceId
- , streamType.c_str()
- , usage.c_str()
- , contentType.c_str()
- , callerName.c_str()
+ , ENUM_EXTRACT(streamTypeForStats)
+ , ENUM_EXTRACT(usageForStats)
+ , ENUM_EXTRACT(contentTypeForStats)
+ , ENUM_EXTRACT(callerNameForStats)
);
}
#endif
@@ -490,7 +564,6 @@
item->get(AMEDIAMETRICS_PROP_NAME, &name);
ALOGD("(key=%s) a2dp connected device:%s atNs:%lld",
key.c_str(), name.c_str(), (long long)atNs);
-
}
void AudioAnalytics::DeviceConnection::createPatch(
@@ -502,27 +575,36 @@
item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH_A2DP") != std::string::npos) {
// TODO compare address
- int64_t timeDiff = item->getTimestamp();
+ int64_t timeDiffNs = item->getTimestamp();
if (mA2dpConnectionRequestNs == 0) {
ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
- timeDiff -= mA2dpConnectionServiceNs;
+ timeDiffNs -= mA2dpConnectionServiceNs;
} else {
- timeDiff -= mA2dpConnectionRequestNs;
+ timeDiffNs -= mA2dpConnectionRequestNs;
}
- ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
+
mA2dpConnectionRequestNs = 0;
mA2dpConnectionServiceNs = 0;
++mA2dpConnectionSuccesses;
+ const auto connectionTimeMs = float(timeDiffNs * 1e-6);
+
+ const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
+ "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
+
+ LOG(DEBUG) << "key:" << key
+ << " A2DP SUCCESS"
+ << " outputDevices:" << outputDeviceBits
+ << " connectionTimeMs:" << connectionTimeMs;
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
+ const long_enum_type_t inputDeviceBits{};
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
- , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__SUCCESS
- , /* connection_time_ms */ timeDiff * 1e-6 /* NS to MS */
+ , ENUM_EXTRACT(inputDeviceBits)
+ , ENUM_EXTRACT(outputDeviceBits)
+ , types::DEVICE_CONNECTION_RESULT_SUCCESS
+ , connectionTimeMs
, /* connection_count */ 1
);
}
@@ -552,18 +634,25 @@
void AudioAnalytics::DeviceConnection::expire() {
std::lock_guard l(mLock);
if (mA2dpConnectionRequestNs == 0) return; // ignore (this was an internal connection).
+
+#ifdef STATSD
+ const long_enum_type_t inputDeviceBits{};
+#endif
+ const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
+ "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
+
if (mA2dpConnectionServiceNs == 0) {
- ALOGD("A2DP device connection service cancels");
++mA2dpConnectionJavaServiceCancels; // service did not connect to A2DP
+ LOG(DEBUG) << "A2DP CANCEL"
+ << " outputDevices:" << outputDeviceBits;
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
- , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__JAVA_SERVICE_CANCEL
+ , ENUM_EXTRACT(inputDeviceBits)
+ , ENUM_EXTRACT(outputDeviceBits)
+ , types::DEVICE_CONNECTION_RESULT_JAVA_SERVICE_CANCEL
, /* connection_time_ms */ 0.f
, /* connection_count */ 1
);
@@ -575,18 +664,19 @@
// AudioFlinger didn't play - an expiration may occur because there is no audio playing.
// Should we check elsewhere?
// TODO: disambiguate this case.
- ALOGD("A2DP device connection expired, state unknown");
mA2dpConnectionRequestNs = 0;
mA2dpConnectionServiceNs = 0;
++mA2dpConnectionUnknowns; // connection result unknown
+
+ LOG(DEBUG) << "A2DP UNKNOWN"
+ << " outputDevices:" << outputDeviceBits;
#ifdef STATSD
if (mAudioAnalytics.mDeliverStatistics) {
(void)android::util::stats_write(
android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
- /* timestamp, */
- /* mediaApexVersion, */
- , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
- , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__UNKNOWN
+ , ENUM_EXTRACT(inputDeviceBits)
+ , ENUM_EXTRACT(outputDeviceBits)
+ , types::DEVICE_CONNECTION_RESULT_UNKNOWN
, /* connection_time_ms */ 0.f
, /* connection_count */ 1
);