blob: 33dfa8fa0fed33daf23089b54ba3047c00f6bbb5 [file] [log] [blame]
Joey Poomarin52989982020-03-05 17:40:49 +08001/*
2 * Copyright (C) 2020 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 "AudioPowerUsage"
19#include <utils/Log.h>
20
21#include "AudioAnalytics.h"
22#include "MediaMetricsService.h"
Andy Hung1ea842e2020-05-18 10:47:31 -070023#include "StringUtils.h"
Joey Poomarin52989982020-03-05 17:40:49 +080024#include <map>
25#include <sstream>
26#include <string>
27#include <audio_utils/clock.h>
28#include <cutils/properties.h>
29#include <statslog.h>
30#include <sys/timerfd.h>
31#include <system/audio-base.h>
32
33// property to disable audio power use metrics feature, default is enabled
34#define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
35#define AUDIO_METRICS_DISABLED_DEFAULT (false)
36
Muhammad Qureshi087b37c2020-06-16 16:37:36 -070037// property to set how long to send audio power use metrics data to statsd, default is 24hrs
Joey Poomarin52989982020-03-05 17:40:49 +080038#define PROP_AUDIO_METRICS_INTERVAL_HR "persist.media.audio_metrics.interval_hr"
39#define INTERVAL_HR_DEFAULT (24)
40
41// for Audio Power Usage Metrics
42#define AUDIO_POWER_USAGE_KEY_AUDIO_USAGE "audio.power.usage"
43
44#define AUDIO_POWER_USAGE_PROP_DEVICE "device" // int32
45#define AUDIO_POWER_USAGE_PROP_DURATION_NS "durationNs" // int64
46#define AUDIO_POWER_USAGE_PROP_TYPE "type" // int32
47#define AUDIO_POWER_USAGE_PROP_VOLUME "volume" // double
48
49namespace android::mediametrics {
50
51/* static */
52bool AudioPowerUsage::typeFromString(const std::string& type_string, int32_t& type) {
53 static std::map<std::string, int32_t> typeTable = {
54 { "AUDIO_STREAM_VOICE_CALL", VOIP_CALL_TYPE },
55 { "AUDIO_STREAM_SYSTEM", MEDIA_TYPE },
56 { "AUDIO_STREAM_RING", RINGTONE_NOTIFICATION_TYPE },
57 { "AUDIO_STREAM_MUSIC", MEDIA_TYPE },
58 { "AUDIO_STREAM_ALARM", ALARM_TYPE },
59 { "AUDIO_STREAM_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
60
61 { "AUDIO_CONTENT_TYPE_SPEECH", VOIP_CALL_TYPE },
62 { "AUDIO_CONTENT_TYPE_MUSIC", MEDIA_TYPE },
63 { "AUDIO_CONTENT_TYPE_MOVIE", MEDIA_TYPE },
64 { "AUDIO_CONTENT_TYPE_SONIFICATION", RINGTONE_NOTIFICATION_TYPE },
65
66 { "AUDIO_USAGE_MEDIA", MEDIA_TYPE },
67 { "AUDIO_USAGE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
68 { "AUDIO_USAGE_ALARM", ALARM_TYPE },
69 { "AUDIO_USAGE_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
70
71 { "AUDIO_SOURCE_CAMCORDER", CAMCORDER_TYPE },
72 { "AUDIO_SOURCE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
73 { "AUDIO_SOURCE_DEFAULT", RECORD_TYPE },
74 { "AUDIO_SOURCE_MIC", RECORD_TYPE },
75 { "AUDIO_SOURCE_UNPROCESSED", RECORD_TYPE },
76 { "AUDIO_SOURCE_VOICE_RECOGNITION", RECORD_TYPE },
77 };
78
79 auto it = typeTable.find(type_string);
80 if (it == typeTable.end()) {
81 type = UNKNOWN_TYPE;
82 return false;
83 }
84
85 type = it->second;
86 return true;
87}
88
89/* static */
90bool AudioPowerUsage::deviceFromString(const std::string& device_string, int32_t& device) {
91 static std::map<std::string, int32_t> deviceTable = {
92 { "AUDIO_DEVICE_OUT_EARPIECE", OUTPUT_EARPIECE },
93 { "AUDIO_DEVICE_OUT_SPEAKER_SAFE", OUTPUT_SPEAKER_SAFE },
94 { "AUDIO_DEVICE_OUT_SPEAKER", OUTPUT_SPEAKER },
95 { "AUDIO_DEVICE_OUT_WIRED_HEADSET", OUTPUT_WIRED_HEADSET },
96 { "AUDIO_DEVICE_OUT_WIRED_HEADPHONE", OUTPUT_WIRED_HEADSET },
97 { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO", OUTPUT_BLUETOOTH_SCO },
98 { "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP", OUTPUT_BLUETOOTH_A2DP },
99 { "AUDIO_DEVICE_OUT_USB_HEADSET", OUTPUT_USB_HEADSET },
100 { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET", OUTPUT_BLUETOOTH_SCO },
101
102 { "AUDIO_DEVICE_IN_BUILTIN_MIC", INPUT_BUILTIN_MIC },
103 { "AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET", INPUT_BLUETOOTH_SCO },
104 { "AUDIO_DEVICE_IN_WIRED_HEADSET", INPUT_WIRED_HEADSET_MIC },
105 { "AUDIO_DEVICE_IN_USB_DEVICE", INPUT_USB_HEADSET_MIC },
106 { "AUDIO_DEVICE_IN_BACK_MIC", INPUT_BUILTIN_BACK_MIC },
107 };
108
109 auto it = deviceTable.find(device_string);
110 if (it == deviceTable.end()) {
111 device = 0;
112 return false;
113 }
114
115 device = it->second;
116 return true;
117}
118
119int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
120 int32_t deviceMask = 0;
Andy Hung1ea842e2020-05-18 10:47:31 -0700121 const auto devaddrvec = stringutils::getDeviceAddressPairs(device_strings);
Joey Poomarin52989982020-03-05 17:40:49 +0800122 for (const auto &[device, addr] : devaddrvec) {
123 int32_t combo_device = 0;
124 deviceFromString(device, combo_device);
125 deviceMask |= combo_device;
126 }
127 return deviceMask;
128}
129
130/* static */
131void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item)
132{
133 int32_t type;
134 if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
135
136 int32_t device;
137 if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &device)) return;
138
139 int64_t duration_ns;
140 if (!item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &duration_ns)) return;
141
142 double volume;
143 if (!item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &volume)) return;
144
Joey Poomarin52989982020-03-05 17:40:49 +0800145 (void)android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
146 device,
147 (int32_t)(duration_ns / NANOS_PER_SECOND),
148 (float)volume,
149 type);
Joey Poomarin52989982020-03-05 17:40:49 +0800150}
151
152bool AudioPowerUsage::saveAsItem_l(
153 int32_t device, int64_t duration_ns, int32_t type, double average_vol)
154{
155 ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
156 (long long)duration_ns, average_vol );
157 if (duration_ns == 0) {
158 return true; // skip duration 0 usage
159 }
160 if (device == 0) {
161 return true; //ignore unknown device
162 }
163
Andy Hung3ab1b322020-05-18 10:47:31 -0700164 for (const auto& item : mItems) {
Joey Poomarin52989982020-03-05 17:40:49 +0800165 int32_t item_type = 0, item_device = 0;
166 double item_volume = 0.;
167 int64_t item_duration_ns = 0;
168 item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &item_device);
169 item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &item_duration_ns);
170 item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &item_type);
171 item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &item_volume);
172
173 // aggregate by device and type
174 if (item_device == device && item_type == type) {
175 int64_t final_duration_ns = item_duration_ns + duration_ns;
176 double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
177 ((item_volume * item_duration_ns +
178 average_vol * duration_ns) / final_duration_ns);
179
180 item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
181 item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
182 item->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
183
184 ALOGV("%s: update (%#x, %d, %lld, %f) --> (%lld, %f)", __func__,
185 device, type,
186 (long long)item_duration_ns, item_volume,
187 (long long)final_duration_ns, final_volume);
188
189 return true;
190 }
191 }
192
193 auto sitem = std::make_shared<mediametrics::Item>(AUDIO_POWER_USAGE_KEY_AUDIO_USAGE);
194 sitem->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
195 sitem->setInt32(AUDIO_POWER_USAGE_PROP_DEVICE, device);
196 sitem->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, duration_ns);
197 sitem->setInt32(AUDIO_POWER_USAGE_PROP_TYPE, type);
198 sitem->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, average_vol);
199 mItems.emplace_back(sitem);
200 return true;
201}
202
rogerfangcfb0ffb2020-07-16 16:27:16 +0800203bool AudioPowerUsage::saveAsItems_l(
204 int32_t device, int64_t duration_ns, int32_t type, double average_vol)
205{
206 ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
207 (long long)duration_ns, average_vol );
208 if (duration_ns == 0) {
209 return true; // skip duration 0 usage
210 }
211 if (device == 0) {
212 return true; //ignore unknown device
213 }
214
215 bool ret = false;
216 const int32_t input_bit = device & INPUT_DEVICE_BIT;
217 int32_t device_bits = device ^ input_bit;
218
219 while (device_bits != 0) {
220 int32_t tmp_device = device_bits & -device_bits; // get lowest bit
221 device_bits ^= tmp_device; // clear lowest bit
222 tmp_device |= input_bit; // restore input bit
223 ret = saveAsItem_l(tmp_device, duration_ns, type, average_vol);
224
225 ALOGV("%s: device %#x recorded, remaining device_bits = %#x", __func__,
226 tmp_device, device_bits);
227 }
228 return ret;
229}
230
Joey Poomarin52989982020-03-05 17:40:49 +0800231void AudioPowerUsage::checkTrackRecord(
232 const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
233{
234 const std::string key = item->getKey();
235
236 int64_t deviceTimeNs = 0;
237 if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
238 return;
239 }
240 double deviceVolume = 1.;
241 if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
242 return;
243 }
244 int32_t type = 0;
245 std::string type_string;
246 if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
247 key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
248 (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
249 key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
250 typeFromString(type_string, type);
251
252 if (isTrack && type == UNKNOWN_TYPE &&
253 mAudioAnalytics->mAnalyticsState->timeMachine().get(
254 key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
255 typeFromString(type_string, type);
256 }
257 if (isTrack && type == UNKNOWN_TYPE &&
258 mAudioAnalytics->mAnalyticsState->timeMachine().get(
259 key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
260 typeFromString(type_string, type);
261 }
262 ALOGV("type = %s => %d", type_string.c_str(), type);
263 }
264
265 int32_t device = 0;
266 std::string device_strings;
267 if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
268 key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
269 (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
270 key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
271
272 device = deviceFromStringPairs(device_strings);
273 ALOGV("device = %s => %d", device_strings.c_str(), device);
274 }
275 std::lock_guard l(mLock);
rogerfangcfb0ffb2020-07-16 16:27:16 +0800276 saveAsItems_l(device, deviceTimeNs, type, deviceVolume);
Joey Poomarin52989982020-03-05 17:40:49 +0800277}
278
279void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
280{
281 std::string mode;
282 if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
283
284 std::lock_guard l(mLock);
285 if (mode == mMode) return; // no change in mode.
286
287 if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
288 const int64_t endCallNs = item->getTimestamp();
289 const int64_t durationNs = endCallNs - mDeviceTimeNs;
290 if (durationNs > 0) {
Andy Hung3ab1b322020-05-18 10:47:31 -0700291 mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
292 mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
rogerfangcfb0ffb2020-07-16 16:27:16 +0800293 saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
Joey Poomarin52989982020-03-05 17:40:49 +0800294 }
295 } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
296 mStartCallNs = item->getTimestamp(); // advisory only
297
298 mDeviceVolume = 0;
299 mVolumeTimeNs = mStartCallNs;
300 mDeviceTimeNs = mStartCallNs;
301 }
302 ALOGV("%s: new mode:%s old mode:%s", __func__, mode.c_str(), mMode.c_str());
303 mMode = mode;
304}
305
306void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
307{
308 double voiceVolume = 0.;
309 if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
310
311 std::lock_guard l(mLock);
312 if (voiceVolume == mVoiceVolume) return; // no change in volume
313
314 // we only track average device volume when we are in-call
315 if (mMode == "AUDIO_MODE_IN_CALL") {
316 const int64_t timeNs = item->getTimestamp();
317 const int64_t durationNs = timeNs - mDeviceTimeNs;
318 if (durationNs > 0) {
Andy Hung3ab1b322020-05-18 10:47:31 -0700319 mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
320 mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
Joey Poomarin52989982020-03-05 17:40:49 +0800321 mVolumeTimeNs = timeNs;
322 }
323 }
324 ALOGV("%s: new voice volume:%lf old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
325 mVoiceVolume = voiceVolume;
326}
327
328void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
329{
330 std::string outputDevices;
331 if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
332
333 const std::string& key = item->getKey();
334 std::string flags;
335 if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
336 key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
337
338 if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
339
340 const int32_t device = deviceFromStringPairs(outputDevices);
341
342 std::lock_guard l(mLock);
343 if (mPrimaryDevice == device) return;
344
345 if (mMode == "AUDIO_MODE_IN_CALL") {
346 // Save statistics
347 const int64_t endDeviceNs = item->getTimestamp();
348 const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
349 if (durationNs > 0) {
Andy Hung3ab1b322020-05-18 10:47:31 -0700350 mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
351 mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
rogerfangcfb0ffb2020-07-16 16:27:16 +0800352 saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
Joey Poomarin52989982020-03-05 17:40:49 +0800353 }
354 // reset statistics
355 mDeviceVolume = 0;
356 mDeviceTimeNs = endDeviceNs;
357 mVolumeTimeNs = endDeviceNs;
358 }
359 ALOGV("%s: new primary device:%#x old primary device:%#x", __func__, device, mPrimaryDevice);
360 mPrimaryDevice = device;
361}
362
363AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
364 : mAudioAnalytics(audioAnalytics)
365 , mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
366 , mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
367{
368 ALOGD("%s", __func__);
369 ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
370 collect(); // send items
371}
372
373AudioPowerUsage::~AudioPowerUsage()
374{
375 ALOGD("%s", __func__);
376}
377
378void AudioPowerUsage::clear()
379{
380 std::lock_guard _l(mLock);
381 mItems.clear();
382}
383
384void AudioPowerUsage::collect()
385{
386 std::lock_guard _l(mLock);
387 for (const auto &item : mItems) {
388 sendItem(item);
389 }
390 mItems.clear();
391 mAudioAnalytics->mTimedAction.postIn(
392 mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
393 [this](){ collect(); });
394}
395
396std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
397 if (limit <= 2) {
398 return {{}, 0};
399 }
400 std::lock_guard _l(mLock);
401 if (mDisabled) {
402 return {"AudioPowerUsage disabled\n", 1};
403 }
404 if (mItems.empty()) {
405 return {"AudioPowerUsage empty\n", 1};
406 }
407
408 int slot = 1;
409 std::stringstream ss;
410 ss << "AudioPowerUsage:\n";
411 for (const auto &item : mItems) {
412 if (slot >= limit - 1) {
413 ss << "-- AudioPowerUsage may be truncated!\n";
414 ++slot;
415 break;
416 }
417 ss << " " << slot << " " << item->toString() << "\n";
418 slot++;
419 }
420 return { ss.str(), slot };
421}
422
Andy Hung3ab1b322020-05-18 10:47:31 -0700423} // namespace android::mediametrics