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 
checkTrackRecord(const std::shared_ptr<const mediametrics::Item> & item,bool isTrack)203 void AudioPowerUsage::checkTrackRecord(
204         const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
205 {
206     const std::string key = item->getKey();
207 
208     int64_t deviceTimeNs = 0;
209     if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
210         return;
211     }
212     double deviceVolume = 1.;
213     if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
214         return;
215     }
216     int32_t type = 0;
217     std::string type_string;
218     if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
219                key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
220         (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
221                key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
222         typeFromString(type_string, type);
223 
224         if (isTrack && type == UNKNOWN_TYPE &&
225                    mAudioAnalytics->mAnalyticsState->timeMachine().get(
226                    key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
227             typeFromString(type_string, type);
228         }
229         if (isTrack && type == UNKNOWN_TYPE &&
230                    mAudioAnalytics->mAnalyticsState->timeMachine().get(
231                    key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
232             typeFromString(type_string, type);
233         }
234         ALOGV("type = %s => %d", type_string.c_str(), type);
235     }
236 
237     int32_t device = 0;
238     std::string device_strings;
239     if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
240          key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
241         (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
242          key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
243 
244         device = deviceFromStringPairs(device_strings);
245         ALOGV("device = %s => %d", device_strings.c_str(), device);
246     }
247     std::lock_guard l(mLock);
248     saveAsItem_l(device, deviceTimeNs, type, deviceVolume);
249 }
250 
checkMode(const std::shared_ptr<const mediametrics::Item> & item)251 void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
252 {
253     std::string mode;
254     if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
255 
256     std::lock_guard l(mLock);
257     if (mode == mMode) return;  // no change in mode.
258 
259     if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
260         const int64_t endCallNs = item->getTimestamp();
261         const int64_t durationNs = endCallNs - mDeviceTimeNs;
262         if (durationNs > 0) {
263             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
264                     mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
265             saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
266         }
267     } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
268         mStartCallNs = item->getTimestamp(); // advisory only
269 
270         mDeviceVolume = 0;
271         mVolumeTimeNs = mStartCallNs;
272         mDeviceTimeNs = mStartCallNs;
273     }
274     ALOGV("%s: new mode:%s  old mode:%s", __func__, mode.c_str(), mMode.c_str());
275     mMode = mode;
276 }
277 
checkVoiceVolume(const std::shared_ptr<const mediametrics::Item> & item)278 void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
279 {
280     double voiceVolume = 0.;
281     if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
282 
283     std::lock_guard l(mLock);
284     if (voiceVolume == mVoiceVolume) return;  // no change in volume
285 
286     // we only track average device volume when we are in-call
287     if (mMode == "AUDIO_MODE_IN_CALL") {
288         const int64_t timeNs = item->getTimestamp();
289         const int64_t durationNs = timeNs - mDeviceTimeNs;
290         if (durationNs > 0) {
291             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
292                     mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
293             mVolumeTimeNs = timeNs;
294         }
295     }
296     ALOGV("%s: new voice volume:%lf  old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
297     mVoiceVolume = voiceVolume;
298 }
299 
checkCreatePatch(const std::shared_ptr<const mediametrics::Item> & item)300 void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
301 {
302     std::string outputDevices;
303     if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
304 
305     const std::string& key = item->getKey();
306     std::string flags;
307     if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
308          key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
309 
310     if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
311 
312     const int32_t device = deviceFromStringPairs(outputDevices);
313 
314     std::lock_guard l(mLock);
315     if (mPrimaryDevice == device) return;
316 
317     if (mMode == "AUDIO_MODE_IN_CALL") {
318         // Save statistics
319         const int64_t endDeviceNs = item->getTimestamp();
320         const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
321         if (durationNs > 0) {
322             mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
323                     mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
324             saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
325         }
326         // reset statistics
327         mDeviceVolume = 0;
328         mDeviceTimeNs = endDeviceNs;
329         mVolumeTimeNs = endDeviceNs;
330     }
331     ALOGV("%s: new primary device:%#x  old primary device:%#x", __func__, device, mPrimaryDevice);
332     mPrimaryDevice = device;
333 }
334 
AudioPowerUsage(AudioAnalytics * audioAnalytics)335 AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
336     : mAudioAnalytics(audioAnalytics)
337     , mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
338     , mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
339 {
340     ALOGD("%s", __func__);
341     ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
342     collect(); // send items
343 }
344 
~AudioPowerUsage()345 AudioPowerUsage::~AudioPowerUsage()
346 {
347     ALOGD("%s", __func__);
348 }
349 
clear()350 void AudioPowerUsage::clear()
351 {
352     std::lock_guard _l(mLock);
353     mItems.clear();
354 }
355 
collect()356 void AudioPowerUsage::collect()
357 {
358     std::lock_guard _l(mLock);
359     for (const auto &item : mItems) {
360         sendItem(item);
361     }
362     mItems.clear();
363     mAudioAnalytics->mTimedAction.postIn(
364         mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
365         [this](){ collect(); });
366 }
367 
dump(int limit) const368 std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
369     if (limit <= 2) {
370         return {{}, 0};
371     }
372     std::lock_guard _l(mLock);
373     if (mDisabled) {
374         return {"AudioPowerUsage disabled\n", 1};
375     }
376     if (mItems.empty()) {
377         return {"AudioPowerUsage empty\n", 1};
378     }
379 
380     int slot = 1;
381     std::stringstream ss;
382     ss << "AudioPowerUsage:\n";
383     for (const auto &item : mItems) {
384         if (slot >= limit - 1) {
385             ss << "-- AudioPowerUsage may be truncated!\n";
386             ++slot;
387             break;
388         }
389         ss << " " << slot << " " << item->toString() << "\n";
390         slot++;
391     }
392     return { ss.str(), slot };
393 }
394 
395 } // namespace android::mediametrics
396