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