• 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-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 
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 
130 /* static */
sendItem(const std::shared_ptr<const mediametrics::Item> & item)131 void 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 
145     (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);
150 }
151 
saveAsItem_l(int32_t device,int64_t duration_ns,int32_t type,double average_vol)152 bool 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 
164     for (const auto& item : mItems) {
165         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 
saveAsItems_l(int32_t device,int64_t duration_ns,int32_t type,double average_vol)203 bool 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 
checkTrackRecord(const std::shared_ptr<const mediametrics::Item> & item,bool isTrack)231 void 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);
276     saveAsItems_l(device, deviceTimeNs, type, deviceVolume);
277 }
278 
checkMode(const std::shared_ptr<const mediametrics::Item> & item)279 void 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) {
291             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
292                     mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
293             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
294         }
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 
checkVoiceVolume(const std::shared_ptr<const mediametrics::Item> & item)306 void 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) {
319             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
320                     mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
321             mVolumeTimeNs = timeNs;
322         }
323     }
324     ALOGV("%s: new voice volume:%lf  old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
325     mVoiceVolume = voiceVolume;
326 }
327 
checkCreatePatch(const std::shared_ptr<const mediametrics::Item> & item)328 void 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) {
350             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
351                     mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
352             saveAsItems_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
353         }
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 
AudioPowerUsage(AudioAnalytics * audioAnalytics)363 AudioPowerUsage::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 
~AudioPowerUsage()373 AudioPowerUsage::~AudioPowerUsage()
374 {
375     ALOGD("%s", __func__);
376 }
377 
clear()378 void AudioPowerUsage::clear()
379 {
380     std::lock_guard _l(mLock);
381     mItems.clear();
382 }
383 
collect()384 void 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 
dump(int limit) const396 std::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 
423 } // namespace android::mediametrics
424