• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "StringUtils.h"
24 #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.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 
37 // property to set how long to send audio power use metrics data to statsd, default is 24hrs
38 #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 
49 namespace android::mediametrics {
50 
51 /* static */
typeFromString(const std::string & type_string,int32_t & type)52 bool 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 */
deviceFromString(const std::string & device_string,int32_t & device)90 bool 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 
deviceFromStringPairs(const std::string & device_strings)119 int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
120     int32_t deviceMask = 0;
121     const auto devaddrvec = stringutils::getDeviceAddressPairs(device_strings);
122     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 
sendItem(const std::shared_ptr<const mediametrics::Item> & item) const130 void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item) const
131 {
132     int32_t type;
133     if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
134 
135     int32_t audio_device;
136     if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &audio_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 
144     const int32_t duration_secs = (int32_t)(duration_ns / NANOS_PER_SECOND);
145     const float average_volume = (float)volume;
146     const int result = android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
147                                          audio_device,
148                                          duration_secs,
149                                          average_volume,
150                                          type);
151 
152     std::stringstream log;
153     log << "result:" << result << " {"
154             << " mediametrics_audio_power_usage_data_reported:"
155             << android::util::AUDIO_POWER_USAGE_DATA_REPORTED
156             << " audio_device:" << audio_device
157             << " duration_secs:" << duration_secs
158             << " average_volume:" << average_volume
159             << " type:" << type
160             << " }";
161     mStatsdLog->log(android::util::AUDIO_POWER_USAGE_DATA_REPORTED, log.str());
162 }
163 
saveAsItem_l(int32_t device,int64_t duration_ns,int32_t type,double average_vol)164 bool AudioPowerUsage::saveAsItem_l(
165         int32_t device, int64_t duration_ns, int32_t type, double average_vol)
166 {
167     ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
168                                    (long long)duration_ns, average_vol );
169     if (duration_ns == 0) {
170         return true; // skip duration 0 usage
171     }
172     if (device == 0) {
173         return true; //ignore unknown device
174     }
175 
176     for (const auto& item : mItems) {
177         int32_t item_type = 0, item_device = 0;
178         double item_volume = 0.;
179         int64_t item_duration_ns = 0;
180         item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &item_device);
181         item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &item_duration_ns);
182         item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &item_type);
183         item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &item_volume);
184 
185         // aggregate by device and type
186         if (item_device == device && item_type == type) {
187             int64_t final_duration_ns = item_duration_ns + duration_ns;
188             double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
189                             ((item_volume * (double)item_duration_ns +
190                             average_vol * (double)duration_ns) / (double)final_duration_ns);
191 
192             item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
193             item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
194             item->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
195 
196             ALOGV("%s: update (%#x, %d, %lld, %f) --> (%lld, %f)", __func__,
197                   device, type,
198                   (long long)item_duration_ns, item_volume,
199                   (long long)final_duration_ns, final_volume);
200 
201             return true;
202         }
203     }
204 
205     auto sitem = std::make_shared<mediametrics::Item>(AUDIO_POWER_USAGE_KEY_AUDIO_USAGE);
206     sitem->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
207     sitem->setInt32(AUDIO_POWER_USAGE_PROP_DEVICE, device);
208     sitem->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, duration_ns);
209     sitem->setInt32(AUDIO_POWER_USAGE_PROP_TYPE, type);
210     sitem->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, average_vol);
211     mItems.emplace_back(sitem);
212     return true;
213 }
214 
saveAsItems_l(int32_t device,int64_t duration_ns,int32_t type,double average_vol)215 bool AudioPowerUsage::saveAsItems_l(
216         int32_t device, int64_t duration_ns, int32_t type, double average_vol)
217 {
218     ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
219                                    (long long)duration_ns, average_vol );
220     if (duration_ns == 0) {
221         return true; // skip duration 0 usage
222     }
223     if (device == 0) {
224         return true; //ignore unknown device
225     }
226 
227     bool ret = false;
228     const int32_t input_bit = device & INPUT_DEVICE_BIT;
229     int32_t device_bits = device ^ input_bit;
230 
231     while (device_bits != 0) {
232         int32_t tmp_device = device_bits & -device_bits; // get lowest bit
233         device_bits ^= tmp_device;  // clear lowest bit
234         tmp_device |= input_bit;    // restore input bit
235         ret = saveAsItem_l(tmp_device, duration_ns, type, average_vol);
236 
237         ALOGV("%s: device %#x recorded, remaining device_bits = %#x", __func__,
238             tmp_device, device_bits);
239     }
240     return ret;
241 }
242 
checkTrackRecord(const std::shared_ptr<const mediametrics::Item> & item,bool isTrack)243 void AudioPowerUsage::checkTrackRecord(
244         const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
245 {
246     const std::string key = item->getKey();
247 
248     int64_t deviceTimeNs = 0;
249     if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
250         return;
251     }
252     double deviceVolume = 1.;
253     if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
254         return;
255     }
256     int32_t type = 0;
257     std::string type_string;
258     if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
259                key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
260         (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
261                key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
262         typeFromString(type_string, type);
263 
264         if (isTrack && type == UNKNOWN_TYPE &&
265                    mAudioAnalytics->mAnalyticsState->timeMachine().get(
266                    key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
267             typeFromString(type_string, type);
268         }
269         if (isTrack && type == UNKNOWN_TYPE &&
270                    mAudioAnalytics->mAnalyticsState->timeMachine().get(
271                    key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
272             typeFromString(type_string, type);
273         }
274         ALOGV("type = %s => %d", type_string.c_str(), type);
275     }
276 
277     int32_t device = 0;
278     std::string device_strings;
279     if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
280          key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
281         (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
282          key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
283 
284         device = deviceFromStringPairs(device_strings);
285         ALOGV("device = %s => %d", device_strings.c_str(), device);
286     }
287     std::lock_guard l(mLock);
288     saveAsItems_l(device, deviceTimeNs, type, deviceVolume);
289 }
290 
checkMode(const std::shared_ptr<const mediametrics::Item> & item)291 void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
292 {
293     std::string mode;
294     if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
295 
296     std::lock_guard l(mLock);
297     if (mode == mMode) return;  // no change in mode.
298 
299     if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
300         const int64_t endCallNs = item->getTimestamp();
301         const int64_t durationNs = endCallNs - mDeviceTimeNs;
302         if (durationNs > 0) {
303             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
304                     mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / (double)durationNs;
305             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
306         }
307     } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
308         mStartCallNs = item->getTimestamp(); // advisory only
309 
310         mDeviceVolume = 0;
311         mVolumeTimeNs = mStartCallNs;
312         mDeviceTimeNs = mStartCallNs;
313     }
314     ALOGV("%s: new mode:%s  old mode:%s", __func__, mode.c_str(), mMode.c_str());
315     mMode = mode;
316 }
317 
checkVoiceVolume(const std::shared_ptr<const mediametrics::Item> & item)318 void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
319 {
320     double voiceVolume = 0.;
321     if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
322 
323     std::lock_guard l(mLock);
324     if (voiceVolume == mVoiceVolume) return;  // no change in volume
325 
326     // we only track average device volume when we are in-call
327     if (mMode == "AUDIO_MODE_IN_CALL") {
328         const int64_t timeNs = item->getTimestamp();
329         const int64_t durationNs = timeNs - mDeviceTimeNs;
330         if (durationNs > 0) {
331             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
332                     mVoiceVolume * double(timeNs - mVolumeTimeNs)) / (double)durationNs;
333             mVolumeTimeNs = timeNs;
334         }
335     }
336     ALOGV("%s: new voice volume:%lf  old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
337     mVoiceVolume = voiceVolume;
338 }
339 
checkCreatePatch(const std::shared_ptr<const mediametrics::Item> & item)340 void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
341 {
342     std::string outputDevices;
343     if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
344 
345     const std::string& key = item->getKey();
346     std::string flags;
347     if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
348          key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
349 
350     if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
351 
352     const int32_t device = deviceFromStringPairs(outputDevices);
353 
354     std::lock_guard l(mLock);
355     if (mPrimaryDevice == device) return;
356 
357     if (mMode == "AUDIO_MODE_IN_CALL") {
358         // Save statistics
359         const int64_t endDeviceNs = item->getTimestamp();
360         const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
361         if (durationNs > 0) {
362             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
363                     mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / (double)durationNs;
364             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
365         }
366         // reset statistics
367         mDeviceVolume = 0;
368         mDeviceTimeNs = endDeviceNs;
369         mVolumeTimeNs = endDeviceNs;
370     }
371     ALOGV("%s: new primary device:%#x  old primary device:%#x", __func__, device, mPrimaryDevice);
372     mPrimaryDevice = device;
373 }
374 
AudioPowerUsage(AudioAnalytics * audioAnalytics,const std::shared_ptr<StatsdLog> & statsdLog)375 AudioPowerUsage::AudioPowerUsage(
376         AudioAnalytics *audioAnalytics, const std::shared_ptr<StatsdLog>& statsdLog)
377     : mAudioAnalytics(audioAnalytics)
378     , mStatsdLog(statsdLog)
379     , mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
380     , mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
381 {
382     ALOGD("%s", __func__);
383     ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
384     collect(); // send items
385 }
386 
~AudioPowerUsage()387 AudioPowerUsage::~AudioPowerUsage()
388 {
389     ALOGD("%s", __func__);
390 }
391 
clear()392 void AudioPowerUsage::clear()
393 {
394     std::lock_guard _l(mLock);
395     mItems.clear();
396 }
397 
collect()398 void AudioPowerUsage::collect()
399 {
400     std::lock_guard _l(mLock);
401     for (const auto &item : mItems) {
402         sendItem(item);
403     }
404     mItems.clear();
405     mAudioAnalytics->mTimedAction.postIn(
406         mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
407         [this](){ collect(); });
408 }
409 
dump(int limit) const410 std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
411     if (limit <= 2) {
412         return {{}, 0};
413     }
414     std::lock_guard _l(mLock);
415     if (mDisabled) {
416         return {"AudioPowerUsage disabled\n", 1};
417     }
418     if (mItems.empty()) {
419         return {"AudioPowerUsage empty\n", 1};
420     }
421 
422     int slot = 1;
423     std::stringstream ss;
424     ss << "AudioPowerUsage:\n";
425     for (const auto &item : mItems) {
426         if (slot >= limit - 1) {
427             ss << "-- AudioPowerUsage may be truncated!\n";
428             ++slot;
429             break;
430         }
431         ss << " " << slot << " " << item->toString() << "\n";
432         slot++;
433     }
434     return { ss.str(), slot };
435 }
436 
437 } // namespace android::mediametrics
438