1c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# Copyright 2014 The Chromium Authors. All rights reserved. 2c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# Use of this source code is governed by a BSD-style license that can be 3c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# found in the LICENSE file. 4c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 5c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport csv 66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import logging 7c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfrom collections import defaultdict 8c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)from telemetry.core.platform.power_monitor import sysfs_power_monitor 106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 11c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 1203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)class DumpsysPowerMonitor(sysfs_power_monitor.SysfsPowerMonitor): 13c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """PowerMonitor that relies on the dumpsys batterystats to monitor the power 14c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch consumption of a single android application. This measure uses a heuristic 15c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch and is the same information end-users see with the battery application. 16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """ 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def __init__(self, device, platform_backend): 18c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """Constructor. 19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 20c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch Args: 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci device: A DeviceUtil instance. 221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci platform_backend: A LinuxBasedPlatformBackend instance. 23c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """ 241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci super(DumpsysPowerMonitor, self).__init__(platform_backend) 255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu self._device = device 26c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 27c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def CanMonitorPower(self): 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return self._device.old_interface.CanControlUsbCharging() 29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def StartMonitoringPower(self, browser): 3103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) super(DumpsysPowerMonitor, self).StartMonitoringPower(browser) 32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # Disable the charging of the device over USB. This is necessary because the 33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # device only collects information about power usage when the device is not 34c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # charging. 355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu self._device.old_interface.DisableUsbCharging() 36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 37c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def StopMonitoringPower(self): 3803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if self._browser: 39c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # pylint: disable=W0212 40c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch package = self._browser._browser_backend.package 4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) cpu_stats = super(DumpsysPowerMonitor, self).StopMonitoringPower() 4203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) self._device.old_interface.EnableUsbCharging() 4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) # By default, 'dumpsys batterystats' measures power consumption during the 4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) # last unplugged period. 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci result = self._platform.RunCommand('dumpsys batterystats -c %s' % package) 4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) assert result, 'Dumpsys produced no output' 4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return super(DumpsysPowerMonitor, self).CombineResults( 4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) cpu_stats, DumpsysPowerMonitor.ParseSamplingOutput(package, result)) 49c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 50c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch @staticmethod 51c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch def ParseSamplingOutput(package, dumpsys_output): 52c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """Parse output of 'dumpsys batterystats -c' 53c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) See: 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/BatteryStats.java 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 57c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch Returns: 58c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch Dictionary in the format returned by StopMonitoringPower(). 59c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch """ 60c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # Raw power usage samples. 61c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch out_dict = {} 62c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch out_dict['identifier'] = 'dumpsys' 63c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch out_dict['power_samples_mw'] = [] 64c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # The list of useful CSV columns. 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index of the column containing the format version. 67c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch DUMP_VERSION_INDEX = 0 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index of the column containing the type of the row. 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ROW_TYPE_INDEX = 3 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index for columns of type unique identifier ('uid') 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index of the column containing the uid. 73c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch PACKAGE_UID_INDEX = 4 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index of the column containing the application package. 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) PACKAGE_NAME_INDEX = 5 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Index for columns of type power use ('pwi') 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # The column containing the uid of the item. 79c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch PWI_UID_INDEX = 1 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # The column containing the type of consumption. Only consumtion since last 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # charge are of interest here. 82c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch PWI_AGGREGATION_INDEX = 2 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # The column containing the amount of power used, in mah. 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) PWI_POWER_COMSUMPTION_INDEX = 5 85c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch csvreader = csv.reader(dumpsys_output) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) uid_entries = {} 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pwi_entries = defaultdict(list) 88c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch for entry in csvreader: 896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if entry[DUMP_VERSION_INDEX] not in ['8', '9']: 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Wrong file version. 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) break 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if ROW_TYPE_INDEX >= len(entry): 93c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch continue 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if entry[ROW_TYPE_INDEX] == 'uid': 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) current_package = entry[PACKAGE_NAME_INDEX] 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) assert current_package not in uid_entries 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) uid_entries[current_package] = entry[PACKAGE_UID_INDEX] 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (PWI_POWER_COMSUMPTION_INDEX < len(entry) and 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) entry[ROW_TYPE_INDEX] == 'pwi' and 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) entry[PWI_AGGREGATION_INDEX] == 'l'): 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pwi_entries[entry[PWI_UID_INDEX]].append( 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) float(entry[PWI_POWER_COMSUMPTION_INDEX])) 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 104c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # Find the uid of for the given package. 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not package in uid_entries: 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logging.warning('Unable to parse dumpsys output. ' + 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Please upgrade the OS version of the device.') 108c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch out_dict['energy_consumption_mwh'] = 0 109c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return out_dict 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) uid = uid_entries[package] 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) consumptions_mah = pwi_entries[uid] 112c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch consumption_mah = sum(consumptions_mah) 113c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # Converting at a nominal voltage of 4.0V, as those values are obtained by a 114c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch # heuristic, and 4.0V is the voltage we set when using a monsoon device. 115c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch consumption_mwh = consumption_mah * 4.0 116c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch out_dict['energy_consumption_mwh'] = consumption_mwh 117c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch return out_dict 118