MediaMetrics: Implement audio power usage metrics

- Trigger audio power usage logging when
   1. endAudioIntervalGroup event for track/record
   2. setMode to NORMAL for telephony
- Aggregate audio usage by type and device
- Send audiousage data every 24hrs by default, using property
  persist.media.audio_power_metrics.interval_hr to config the interval
- Add property persist.media.audio_power_metrics.disable to disable
  audio power metrics

Bug: 121318157
Bug: 140909217
Test: atest mediametrics_tests
Test: adb shell dumpsys media.metrics --all
Change-Id: I94ff00a5609a49c96a14827d3260d481c1310abe
Signed-off-by: Robert Lee <lerobert@google.com>
gugelfrei
Joey Poomarin 4 years ago committed by Robert Lee
parent ad531ce5d9
commit 5298998e57

@ -92,6 +92,7 @@
#define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
#define AMEDIAMETRICS_PROP_ALLOWUID "_allowUid" // int32_t, allow client uid to post
#define AMEDIAMETRICS_PROP_AUDIOMODE "audioMode" // string (audio.flinger)
#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
#define AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES "bufferSizeFrames" // int32
#define AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES "bufferCapacityFrames" // int32
@ -146,6 +147,7 @@
#define AMEDIAMETRICS_PROP_UNDERRUN "underrun" // int32
#define AMEDIAMETRICS_PROP_UNDERRUNFRAMES "underrunFrames" // int64_t from Thread
#define AMEDIAMETRICS_PROP_USAGE "usage" // string attributes (ATrack)
#define AMEDIAMETRICS_PROP_VOICEVOLUME "voiceVolume" // double (audio.flinger)
#define AMEDIAMETRICS_PROP_VOLUME_LEFT "volume.left" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_VOLUME_RIGHT "volume.right" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_WHERE "where" // string value
@ -170,7 +172,9 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE "pause" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE "setMode" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_START "start" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord

@ -1176,6 +1176,10 @@ status_t AudioFlinger::setMode(audio_mode_t mode)
mPlaybackThreads.valueAt(i)->setMode(mode);
}
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE)
.set(AMEDIAMETRICS_PROP_AUDIOMODE, toString(mode))
.record();
return ret;
}
@ -1741,6 +1745,10 @@ status_t AudioFlinger::setVoiceVolume(float value)
ret = dev->setVoiceVolume(value);
mHardwareStatus = AUDIO_HW_IDLE;
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME)
.set(AMEDIAMETRICS_PROP_VOICEVOLUME, (double)value)
.record();
return ret;
}

@ -36,6 +36,7 @@ cc_library_shared {
srcs: [
"AudioAnalytics.cpp",
"AudioPowerUsage.cpp",
"iface_statsd.cpp",
"MediaMetricsService.cpp",
"statsd_audiopolicy.cpp",
@ -55,6 +56,7 @@ cc_library_shared {
shared_libs: [
"libbinder",
"libcutils",
"liblog",
"libmediametrics",
"libmediautils",

@ -132,6 +132,49 @@ AudioAnalytics::AudioAnalytics()
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mDeviceConnection.createPatch(item);
}));
// Handle power usage
mActions.addAction(
AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mAudioPowerUsage.checkTrackRecord(item, true /* isTrack */);
}));
mActions.addAction(
AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mAudioPowerUsage.checkTrackRecord(item, false /* isTrack */);
}));
mActions.addAction(
AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
// ALOGD("(key=%s) Audioflinger setMode", item->getKey().c_str());
mAudioPowerUsage.checkMode(item);
}));
mActions.addAction(
AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
// ALOGD("(key=%s) Audioflinger setVoiceVolume", item->getKey().c_str());
mAudioPowerUsage.checkVoiceVolume(item);
}));
mActions.addAction(
AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
std::string("createAudioPatch"),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mAudioPowerUsage.checkCreatePatch(item);
}));
}
AudioAnalytics::~AudioAnalytics()
@ -173,6 +216,12 @@ std::pair<std::string, int32_t> AudioAnalytics::dump(
ss << s;
ll -= l;
}
if (ll > 0 && prefix == nullptr) {
auto [s, l] = mAudioPowerUsage.dump(ll);
ss << s;
ll -= l;
}
return { ss.str(), lines - ll };
}

@ -19,6 +19,7 @@
#include <android-base/thread_annotations.h>
#include "AnalyticsActions.h"
#include "AnalyticsState.h"
#include "AudioPowerUsage.h"
#include "TimedAction.h"
#include "Wrap.h"
@ -26,6 +27,9 @@ namespace android::mediametrics {
class AudioAnalytics
{
// AudioAnalytics action / state helper classes
friend AudioPowerUsage;
public:
AudioAnalytics();
~AudioAnalytics();
@ -72,6 +76,9 @@ public:
// underlying state is locked.
mPreviousAnalyticsState->clear();
mAnalyticsState->clear();
// Clear power usage state.
mAudioPowerUsage.clear();
}
private:
@ -149,6 +156,8 @@ private:
int32_t mA2dpConnectedSuccesses GUARDED_BY(mLock) = 0;
int32_t mA2dpConnectedFailures GUARDED_BY(mLock) = 0;
} mDeviceConnection{*this};
AudioPowerUsage mAudioPowerUsage{this};
};
} // namespace android::mediametrics

@ -0,0 +1,396 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioPowerUsage"
#include <utils/Log.h>
#include "AudioAnalytics.h"
#include "MediaMetricsService.h"
#include <map>
#include <sstream>
#include <string>
#include <audio_utils/clock.h>
#include <cutils/properties.h>
#include <statslog.h>
#include <sys/timerfd.h>
#include <system/audio-base.h>
// property to disable audio power use metrics feature, default is enabled
#define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
#define AUDIO_METRICS_DISABLED_DEFAULT (false)
// property to set how long to send audio power use metrics data to westworld, default is 24hrs
#define PROP_AUDIO_METRICS_INTERVAL_HR "persist.media.audio_metrics.interval_hr"
#define INTERVAL_HR_DEFAULT (24)
// for Audio Power Usage Metrics
#define AUDIO_POWER_USAGE_KEY_AUDIO_USAGE "audio.power.usage"
#define AUDIO_POWER_USAGE_PROP_DEVICE "device" // int32
#define AUDIO_POWER_USAGE_PROP_DURATION_NS "durationNs" // int64
#define AUDIO_POWER_USAGE_PROP_TYPE "type" // int32
#define AUDIO_POWER_USAGE_PROP_VOLUME "volume" // double
namespace android::mediametrics {
/* static */
bool AudioPowerUsage::typeFromString(const std::string& type_string, int32_t& type) {
static std::map<std::string, int32_t> typeTable = {
{ "AUDIO_STREAM_VOICE_CALL", VOIP_CALL_TYPE },
{ "AUDIO_STREAM_SYSTEM", MEDIA_TYPE },
{ "AUDIO_STREAM_RING", RINGTONE_NOTIFICATION_TYPE },
{ "AUDIO_STREAM_MUSIC", MEDIA_TYPE },
{ "AUDIO_STREAM_ALARM", ALARM_TYPE },
{ "AUDIO_STREAM_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
{ "AUDIO_CONTENT_TYPE_SPEECH", VOIP_CALL_TYPE },
{ "AUDIO_CONTENT_TYPE_MUSIC", MEDIA_TYPE },
{ "AUDIO_CONTENT_TYPE_MOVIE", MEDIA_TYPE },
{ "AUDIO_CONTENT_TYPE_SONIFICATION", RINGTONE_NOTIFICATION_TYPE },
{ "AUDIO_USAGE_MEDIA", MEDIA_TYPE },
{ "AUDIO_USAGE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
{ "AUDIO_USAGE_ALARM", ALARM_TYPE },
{ "AUDIO_USAGE_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
{ "AUDIO_SOURCE_CAMCORDER", CAMCORDER_TYPE },
{ "AUDIO_SOURCE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
{ "AUDIO_SOURCE_DEFAULT", RECORD_TYPE },
{ "AUDIO_SOURCE_MIC", RECORD_TYPE },
{ "AUDIO_SOURCE_UNPROCESSED", RECORD_TYPE },
{ "AUDIO_SOURCE_VOICE_RECOGNITION", RECORD_TYPE },
};
auto it = typeTable.find(type_string);
if (it == typeTable.end()) {
type = UNKNOWN_TYPE;
return false;
}
type = it->second;
return true;
}
/* static */
bool AudioPowerUsage::deviceFromString(const std::string& device_string, int32_t& device) {
static std::map<std::string, int32_t> deviceTable = {
{ "AUDIO_DEVICE_OUT_EARPIECE", OUTPUT_EARPIECE },
{ "AUDIO_DEVICE_OUT_SPEAKER_SAFE", OUTPUT_SPEAKER_SAFE },
{ "AUDIO_DEVICE_OUT_SPEAKER", OUTPUT_SPEAKER },
{ "AUDIO_DEVICE_OUT_WIRED_HEADSET", OUTPUT_WIRED_HEADSET },
{ "AUDIO_DEVICE_OUT_WIRED_HEADPHONE", OUTPUT_WIRED_HEADSET },
{ "AUDIO_DEVICE_OUT_BLUETOOTH_SCO", OUTPUT_BLUETOOTH_SCO },
{ "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP", OUTPUT_BLUETOOTH_A2DP },
{ "AUDIO_DEVICE_OUT_USB_HEADSET", OUTPUT_USB_HEADSET },
{ "AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET", OUTPUT_BLUETOOTH_SCO },
{ "AUDIO_DEVICE_IN_BUILTIN_MIC", INPUT_BUILTIN_MIC },
{ "AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET", INPUT_BLUETOOTH_SCO },
{ "AUDIO_DEVICE_IN_WIRED_HEADSET", INPUT_WIRED_HEADSET_MIC },
{ "AUDIO_DEVICE_IN_USB_DEVICE", INPUT_USB_HEADSET_MIC },
{ "AUDIO_DEVICE_IN_BACK_MIC", INPUT_BUILTIN_BACK_MIC },
};
auto it = deviceTable.find(device_string);
if (it == deviceTable.end()) {
device = 0;
return false;
}
device = it->second;
return true;
}
int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
int32_t deviceMask = 0;
const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(device_strings);
for (const auto &[device, addr] : devaddrvec) {
int32_t combo_device = 0;
deviceFromString(device, combo_device);
deviceMask |= combo_device;
}
return deviceMask;
}
/* static */
void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item)
{
int32_t type;
if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
int32_t device;
if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &device)) return;
int64_t duration_ns;
if (!item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &duration_ns)) return;
double volume;
if (!item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &volume)) return;
#ifdef STATSD
(void)android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
device,
(int32_t)(duration_ns / NANOS_PER_SECOND),
(float)volume,
type);
#endif
}
bool AudioPowerUsage::saveAsItem_l(
int32_t device, int64_t duration_ns, int32_t type, double average_vol)
{
ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
(long long)duration_ns, average_vol );
if (duration_ns == 0) {
return true; // skip duration 0 usage
}
if (device == 0) {
return true; //ignore unknown device
}
for (auto item : mItems) {
int32_t item_type = 0, item_device = 0;
double item_volume = 0.;
int64_t item_duration_ns = 0;
item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &item_device);
item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &item_duration_ns);
item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &item_type);
item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &item_volume);
// aggregate by device and type
if (item_device == device && item_type == type) {
int64_t final_duration_ns = item_duration_ns + duration_ns;
double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
((item_volume * item_duration_ns +
average_vol * duration_ns) / final_duration_ns);
item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
item->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
ALOGV("%s: update (%#x, %d, %lld, %f) --> (%lld, %f)", __func__,
device, type,
(long long)item_duration_ns, item_volume,
(long long)final_duration_ns, final_volume);
return true;
}
}
auto sitem = std::make_shared<mediametrics::Item>(AUDIO_POWER_USAGE_KEY_AUDIO_USAGE);
sitem->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
sitem->setInt32(AUDIO_POWER_USAGE_PROP_DEVICE, device);
sitem->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, duration_ns);
sitem->setInt32(AUDIO_POWER_USAGE_PROP_TYPE, type);
sitem->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, average_vol);
mItems.emplace_back(sitem);
return true;
}
void AudioPowerUsage::checkTrackRecord(
const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
{
const std::string key = item->getKey();
int64_t deviceTimeNs = 0;
if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
return;
}
double deviceVolume = 1.;
if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
return;
}
int32_t type = 0;
std::string type_string;
if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
(!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
typeFromString(type_string, type);
if (isTrack && type == UNKNOWN_TYPE &&
mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
typeFromString(type_string, type);
}
if (isTrack && type == UNKNOWN_TYPE &&
mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
typeFromString(type_string, type);
}
ALOGV("type = %s => %d", type_string.c_str(), type);
}
int32_t device = 0;
std::string device_strings;
if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
(!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
device = deviceFromStringPairs(device_strings);
ALOGV("device = %s => %d", device_strings.c_str(), device);
}
std::lock_guard l(mLock);
saveAsItem_l(device, deviceTimeNs, type, deviceVolume);
}
void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
{
std::string mode;
if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
std::lock_guard l(mLock);
if (mode == mMode) return; // no change in mode.
if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
const int64_t endCallNs = item->getTimestamp();
const int64_t durationNs = endCallNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
mVoiceVolume * (endCallNs - mVolumeTimeNs)) / durationNs;
saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
} else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
mStartCallNs = item->getTimestamp(); // advisory only
mDeviceVolume = 0;
mVolumeTimeNs = mStartCallNs;
mDeviceTimeNs = mStartCallNs;
}
ALOGV("%s: new mode:%s old mode:%s", __func__, mode.c_str(), mMode.c_str());
mMode = mode;
}
void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
{
double voiceVolume = 0.;
if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
std::lock_guard l(mLock);
if (voiceVolume == mVoiceVolume) return; // no change in volume
// we only track average device volume when we are in-call
if (mMode == "AUDIO_MODE_IN_CALL") {
const int64_t timeNs = item->getTimestamp();
const int64_t durationNs = timeNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
mVoiceVolume * (timeNs - mVolumeTimeNs)) / durationNs;
mVolumeTimeNs = timeNs;
}
}
ALOGV("%s: new voice volume:%lf old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
mVoiceVolume = voiceVolume;
}
void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
{
std::string outputDevices;
if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
const std::string& key = item->getKey();
std::string flags;
if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
const int32_t device = deviceFromStringPairs(outputDevices);
std::lock_guard l(mLock);
if (mPrimaryDevice == device) return;
if (mMode == "AUDIO_MODE_IN_CALL") {
// Save statistics
const int64_t endDeviceNs = item->getTimestamp();
const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
if (durationNs > 0) {
mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
mVoiceVolume * (endDeviceNs - mVolumeTimeNs)) / durationNs;
saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
}
// reset statistics
mDeviceVolume = 0;
mDeviceTimeNs = endDeviceNs;
mVolumeTimeNs = endDeviceNs;
}
ALOGV("%s: new primary device:%#x old primary device:%#x", __func__, device, mPrimaryDevice);
mPrimaryDevice = device;
}
AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
: mAudioAnalytics(audioAnalytics)
, mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
, mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
{
ALOGD("%s", __func__);
ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
collect(); // send items
}
AudioPowerUsage::~AudioPowerUsage()
{
ALOGD("%s", __func__);
}
void AudioPowerUsage::clear()
{
std::lock_guard _l(mLock);
mItems.clear();
}
void AudioPowerUsage::collect()
{
std::lock_guard _l(mLock);
for (const auto &item : mItems) {
sendItem(item);
}
mItems.clear();
mAudioAnalytics->mTimedAction.postIn(
mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
[this](){ collect(); });
}
std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
if (limit <= 2) {
return {{}, 0};
}
std::lock_guard _l(mLock);
if (mDisabled) {
return {"AudioPowerUsage disabled\n", 1};
}
if (mItems.empty()) {
return {"AudioPowerUsage empty\n", 1};
}
int slot = 1;
std::stringstream ss;
ss << "AudioPowerUsage:\n";
for (const auto &item : mItems) {
if (slot >= limit - 1) {
ss << "-- AudioPowerUsage may be truncated!\n";
++slot;
break;
}
ss << " " << slot << " " << item->toString() << "\n";
slot++;
}
return { ss.str(), slot };
}
} // namespace android

@ -0,0 +1,105 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/thread_annotations.h>
#include <deque>
#include <media/MediaMetricsItem.h>
#include <mutex>
#include <thread>
namespace android::mediametrics {
class AudioAnalytics;
class AudioPowerUsage {
public:
explicit AudioPowerUsage(AudioAnalytics *audioAnalytics);
~AudioPowerUsage();
void checkTrackRecord(const std::shared_ptr<const mediametrics::Item>& item, bool isTrack);
void checkMode(const std::shared_ptr<const mediametrics::Item>& item);
void checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item);
void checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item);
void clear();
/**
* Returns a pair consisting of the dump string, and the number of lines in the string.
*
* The number of lines in the returned pair is used as an optimization
* for subsequent line limiting.
*
* \param lines the maximum number of lines in the string returned.
*/
std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
// align with message AudioUsageDataReported in frameworks/base/cmds/statsd/src/atoms.proto
enum AudioType {
UNKNOWN_TYPE = 0,
VOICE_CALL_TYPE = 1, // voice call
VOIP_CALL_TYPE = 2, // voip call, including uplink and downlink
MEDIA_TYPE = 3, // music and system sound
RINGTONE_NOTIFICATION_TYPE = 4, // ringtone and notification
ALARM_TYPE = 5, // alarm type
// record type
CAMCORDER_TYPE = 6, // camcorder
RECORD_TYPE = 7, // other recording
};
enum AudioDevice {
OUTPUT_EARPIECE = 0x1,
OUTPUT_SPEAKER = 0x2,
OUTPUT_WIRED_HEADSET = 0x4,
OUTPUT_USB_HEADSET = 0x8,
OUTPUT_BLUETOOTH_SCO = 0x10,
OUTPUT_BLUETOOTH_A2DP = 0x20,
OUTPUT_SPEAKER_SAFE = 0x40,
INPUT_DEVICE_BIT = 0x40000000,
INPUT_BUILTIN_MIC = INPUT_DEVICE_BIT | 0x1, // non-negative positive int32.
INPUT_BUILTIN_BACK_MIC = INPUT_DEVICE_BIT | 0x2,
INPUT_WIRED_HEADSET_MIC = INPUT_DEVICE_BIT | 0x4,
INPUT_USB_HEADSET_MIC = INPUT_DEVICE_BIT | 0x8,
INPUT_BLUETOOTH_SCO = INPUT_DEVICE_BIT | 0x10,
};
static bool typeFromString(const std::string& type_string, int32_t& type);
static bool deviceFromString(const std::string& device_string, int32_t& device);
static int32_t deviceFromStringPairs(const std::string& device_strings);
private:
bool saveAsItem_l(int32_t device, int64_t duration, int32_t type, double average_vol)
REQUIRES(mLock);
static void sendItem(const std::shared_ptr<const mediametrics::Item>& item);
void collect();
AudioAnalytics * const mAudioAnalytics;
const bool mDisabled;
const int32_t mIntervalHours;
mutable std::mutex mLock;
std::deque<std::shared_ptr<mediametrics::Item>> mItems GUARDED_BY(mLock);
double mVoiceVolume GUARDED_BY(mLock) = 0.;
double mDeviceVolume GUARDED_BY(mLock) = 0.;
int64_t mStartCallNs GUARDED_BY(mLock) = 0; // advisory only
int64_t mVolumeTimeNs GUARDED_BY(mLock) = 0;
int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
int32_t mPrimaryDevice GUARDED_BY(mLock) = OUTPUT_SPEAKER;
std::string mMode GUARDED_BY(mLock) {"AUDIO_MODE_NORMAL"};
};
} // namespace android::mediametrics

@ -803,7 +803,7 @@ TEST(mediametrics_tests, audio_analytics_permission) {
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@ -839,7 +839,7 @@ TEST(mediametrics_tests, audio_analytics_permission2) {
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key

Loading…
Cancel
Save