Commit 01041e84 authored by rnephew's avatar rnephew Committed by Commit bot

[Android] Move implementation of battery utils from device utils

Move the implementation of doing power related actions from device_utils to battery_utils.

BUG=472763

Review URL: https://codereview.chromium.org/1067853003

Cr-Commit-Position: refs/heads/master@{#324094}
parent 1ad64324
...@@ -147,7 +147,6 @@ class BatteryUtils(object): ...@@ -147,7 +147,6 @@ class BatteryUtils(object):
""" """
return self.GetPowerData().get(package) return self.GetPowerData().get(package)
# TODO(rnephew): Move implementation from device_utils when this is used.
def GetBatteryInfo(self, timeout=None, retries=None): def GetBatteryInfo(self, timeout=None, retries=None):
"""Gets battery info for the device. """Gets battery info for the device.
...@@ -158,9 +157,21 @@ class BatteryUtils(object): ...@@ -158,9 +157,21 @@ class BatteryUtils(object):
A dict containing various battery information as reported by dumpsys A dict containing various battery information as reported by dumpsys
battery. battery.
""" """
return self._device.GetBatteryInfo(timeout=None, retries=None) result = {}
# Skip the first line, which is just a header.
for line in self._device.RunShellCommand(
['dumpsys', 'battery'], check_return=True)[1:]:
# If usb charging has been disabled, an extra line of header exists.
if 'UPDATES STOPPED' in line:
logging.warning('Dumpsys battery not receiving updates. '
'Run dumpsys battery reset if this is in error.')
elif ':' not in line:
logging.warning('Unknown line found in dumpsys battery: "%s"', line)
else:
k, v = line.split(':', 1)
result[k.strip()] = v.strip()
return result
# TODO(rnephew): Move implementation from device_utils when this is used.
def GetCharging(self, timeout=None, retries=None): def GetCharging(self, timeout=None, retries=None):
"""Gets the charging state of the device. """Gets the charging state of the device.
...@@ -170,9 +181,13 @@ class BatteryUtils(object): ...@@ -170,9 +181,13 @@ class BatteryUtils(object):
Returns: Returns:
True if the device is charging, false otherwise. True if the device is charging, false otherwise.
""" """
return self._device.GetCharging(timeout=None, retries=None) battery_info = self.GetBatteryInfo()
for k in ('AC powered', 'USB powered', 'Wireless powered'):
if (k in battery_info and
battery_info[k].lower() in ('true', '1', 'yes')):
return True
return False
# TODO(rnephew): Move implementation from device_utils when this is used.
def SetCharging(self, enabled, timeout=None, retries=None): def SetCharging(self, enabled, timeout=None, retries=None):
"""Enables or disables charging on the device. """Enables or disables charging on the device.
...@@ -186,9 +201,26 @@ class BatteryUtils(object): ...@@ -186,9 +201,26 @@ class BatteryUtils(object):
device_errors.CommandFailedError: If method of disabling charging cannot device_errors.CommandFailedError: If method of disabling charging cannot
be determined. be determined.
""" """
self._device.SetCharging(enabled, timeout=None, retries=None) if 'charging_config' not in self._cache:
for c in _CONTROL_CHARGING_COMMANDS:
if self._device.FileExists(c['witness_file']):
self._cache['charging_config'] = c
break
else:
raise device_errors.CommandFailedError(
'Unable to find charging commands.')
if enabled:
command = self._cache['charging_config']['enable_command']
else:
command = self._cache['charging_config']['disable_command']
def set_and_verify_charging():
self._device.RunShellCommand(command, check_return=True)
return self.GetCharging() == enabled
timeout_retry.WaitFor(set_and_verify_charging, wait_period=1)
# TODO(rnephew): Move implementation from device_utils when this is used.
# TODO(rnephew): Make private when all use cases can use the context manager. # TODO(rnephew): Make private when all use cases can use the context manager.
def DisableBatteryUpdates(self, timeout=None, retries=None): def DisableBatteryUpdates(self, timeout=None, retries=None):
""" Resets battery data and makes device appear like it is not """ Resets battery data and makes device appear like it is not
...@@ -202,9 +234,28 @@ class BatteryUtils(object): ...@@ -202,9 +234,28 @@ class BatteryUtils(object):
device_errors.CommandFailedError: When resetting batterystats fails to device_errors.CommandFailedError: When resetting batterystats fails to
reset power values. reset power values.
""" """
self._device.DisableBatteryUpdates(timeout=None, retries=None) def battery_updates_disabled():
return self.GetCharging() is False
self._device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True)
self._device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True)
battery_data = self._device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '--checkin'],
check_return=True)
ROW_TYPE_INDEX = 3
PWI_POWER_INDEX = 5
for line in battery_data:
l = line.split(',')
if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi'
and l[PWI_POWER_INDEX] != 0):
raise device_errors.CommandFailedError(
'Non-zero pmi value found after reset.')
self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'],
check_return=True)
timeout_retry.WaitFor(battery_updates_disabled, wait_period=1)
# TODO(rnephew): Move implementation from device_utils when this is used.
# TODO(rnephew): Make private when all use cases can use the context manager. # TODO(rnephew): Make private when all use cases can use the context manager.
def EnableBatteryUpdates(self, timeout=None, retries=None): def EnableBatteryUpdates(self, timeout=None, retries=None):
""" Restarts device charging so that dumpsys no longer collects power data. """ Restarts device charging so that dumpsys no longer collects power data.
...@@ -213,7 +264,14 @@ class BatteryUtils(object): ...@@ -213,7 +264,14 @@ class BatteryUtils(object):
timeout: timeout in seconds timeout: timeout in seconds
retries: number of retries retries: number of retries
""" """
self._device.EnableBatteryUpdates(timeout=None, retries=None) def battery_updates_enabled():
return self.GetCharging() is True
self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'],
check_return=True)
self._device.RunShellCommand(['dumpsys', 'battery', 'reset'],
check_return=True)
timeout_retry.WaitFor(battery_updates_enabled, wait_period=1)
@contextlib.contextmanager @contextlib.contextmanager
def BatteryMeasurement(self, timeout=None, retries=None): def BatteryMeasurement(self, timeout=None, retries=None):
......
...@@ -64,16 +64,16 @@ class BatteryUtilsSetChargingTest(BatteryUtilsTest): ...@@ -64,16 +64,16 @@ class BatteryUtilsSetChargingTest(BatteryUtilsTest):
with self.assertCalls( with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True), (self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []), (self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), False), (self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []), (self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True)): (self.call.battery.GetCharging(), True)):
self.battery.SetCharging(True) self.battery.SetCharging(True)
def testSetCharging_alreadyEnabled(self): def testSetCharging_alreadyEnabled(self):
with self.assertCalls( with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True), (self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []), (self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True)): (self.call.battery.GetCharging(), True)):
self.battery.SetCharging(True) self.battery.SetCharging(True)
@mock.patch('time.sleep', mock.Mock()) @mock.patch('time.sleep', mock.Mock())
...@@ -81,9 +81,9 @@ class BatteryUtilsSetChargingTest(BatteryUtilsTest): ...@@ -81,9 +81,9 @@ class BatteryUtilsSetChargingTest(BatteryUtilsTest):
with self.assertCalls( with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True), (self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []), (self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True), (self.call.battery.GetCharging(), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []), (self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), False)): (self.call.battery.GetCharging(), False)):
self.battery.SetCharging(False) self.battery.SetCharging(False)
...@@ -94,6 +94,8 @@ class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest): ...@@ -94,6 +94,8 @@ class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
mock.ANY, retries=0, single_line=True, mock.ANY, retries=0, single_line=True,
timeout=10, check_return=True), '22'), timeout=10, check_return=True), '22'),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True), []),
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True), []), ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
...@@ -101,12 +103,12 @@ class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest): ...@@ -101,12 +103,12 @@ class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
check_return=True), []), check_return=True), []),
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []), ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
(self.call.device.GetCharging(), False), (self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True), []), ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True), []),
(self.call.device.RunShellCommand( (self.call.device.RunShellCommand(
['dumpsys', 'battery', 'reset'], check_return=True), []), ['dumpsys', 'battery', 'reset'], check_return=True), []),
(self.call.device.GetCharging(), True)): (self.call.battery.GetCharging(), True)):
with self.battery.BatteryMeasurement(): with self.battery.BatteryMeasurement():
pass pass
...@@ -152,6 +154,63 @@ class BatteryUtilsChargeDevice(BatteryUtilsTest): ...@@ -152,6 +154,63 @@ class BatteryUtilsChargeDevice(BatteryUtilsTest):
self.battery.ChargeDeviceToLevel(95) self.battery.ChargeDeviceToLevel(95)
class DeviceUtilsGetBatteryInfoTest(BatteryUtilsTest):
def testGetBatteryInfo_normal(self):
with self.assertCall(
self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True),
[
'Current Battery Service state:',
' AC powered: false',
' USB powered: true',
' level: 100',
' temperature: 321',
]):
self.assertEquals(
{
'AC powered': 'false',
'USB powered': 'true',
'level': '100',
'temperature': '321',
},
self.battery.GetBatteryInfo())
def testGetBatteryInfo_nothing(self):
with self.assertCall(
self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), []):
self.assertEquals({}, self.battery.GetBatteryInfo())
class DeviceUtilsGetChargingTest(BatteryUtilsTest):
def testGetCharging_usb(self):
with self.assertCall(
self.call.battery.GetBatteryInfo(), {'USB powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_usbFalse(self):
with self.assertCall(
self.call.battery.GetBatteryInfo(), {'USB powered': 'false'}):
self.assertFalse(self.battery.GetCharging())
def testGetCharging_ac(self):
with self.assertCall(
self.call.battery.GetBatteryInfo(), {'AC powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_wireless(self):
with self.assertCall(
self.call.battery.GetBatteryInfo(), {'Wireless powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_unknown(self):
with self.assertCall(
self.call.battery.GetBatteryInfo(), {'level': '42'}):
self.assertFalse(self.battery.GetCharging())
if __name__ == '__main__': if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2) unittest.main(verbosity=2)
...@@ -1439,159 +1439,6 @@ class DeviceUtils(object): ...@@ -1439,159 +1439,6 @@ class DeviceUtils(object):
""" """
return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
# TODO(rnephew): Remove when battery_utils is switched to.
@decorators.WithTimeoutAndRetriesFromInstance()
def GetBatteryInfo(self, timeout=None, retries=None):
"""Gets battery info for the device.
Args:
timeout: timeout in seconds
retries: number of retries
Returns:
A dict containing various battery information as reported by dumpsys
battery.
"""
result = {}
# Skip the first line, which is just a header.
for line in self.RunShellCommand(
['dumpsys', 'battery'], check_return=True)[1:]:
# If usb charging has been disabled, an extra line of header exists.
if 'UPDATES STOPPED' in line:
logging.warning('Dumpsys battery not receiving updates. '
'Run dumpsys battery reset if this is in error.')
elif ':' not in line:
logging.warning('Unknown line found in dumpsys battery.')
logging.warning(line)
else:
k, v = line.split(': ', 1)
result[k.strip()] = v.strip()
return result
# TODO(rnephew): Remove when battery_utils is switched to.
@decorators.WithTimeoutAndRetriesFromInstance()
def GetCharging(self, timeout=None, retries=None):
"""Gets the charging state of the device.
Args:
timeout: timeout in seconds
retries: number of retries
Returns:
True if the device is charging, false otherwise.
"""
battery_info = self.GetBatteryInfo()
for k in ('AC powered', 'USB powered', 'Wireless powered'):
if (k in battery_info and
battery_info[k].lower() in ('true', '1', 'yes')):
return True
return False
# TODO(rnephew): Remove when battery_utils is switched to.
@decorators.WithTimeoutAndRetriesFromInstance()
def SetCharging(self, enabled, timeout=None, retries=None):
"""Enables or disables charging on the device.
Args:
enabled: A boolean indicating whether charging should be enabled or
disabled.
timeout: timeout in seconds
retries: number of retries
"""
if 'charging_config' not in self._cache:
for c in _CONTROL_CHARGING_COMMANDS:
if self.FileExists(c['witness_file']):
self._cache['charging_config'] = c
break
else:
raise device_errors.CommandFailedError(
'Unable to find charging commands.')
if enabled:
command = self._cache['charging_config']['enable_command']
else:
command = self._cache['charging_config']['disable_command']
def set_and_verify_charging():
self.RunShellCommand(command, check_return=True)
return self.GetCharging() == enabled
timeout_retry.WaitFor(set_and_verify_charging, wait_period=1)
# TODO(rnephew): Remove when battery_utils is switched to.
@decorators.WithTimeoutAndRetriesFromInstance()
def DisableBatteryUpdates(self, timeout=None, retries=None):
""" Resets battery data and makes device appear like it is not
charging so that it will collect power data since last charge.
Args:
timeout: timeout in seconds
retries: number of retries
"""
def battery_updates_disabled():
return self.GetCharging() is False
self.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True)
battery_data = self.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '--checkin'],
check_return=True)
ROW_TYPE_INDEX = 3
PWI_POWER_INDEX = 5
for line in battery_data:
l = line.split(',')
if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi'
and l[PWI_POWER_INDEX] != 0):
raise device_errors.CommandFailedError(
'Non-zero pmi value found after reset.')
self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'],
check_return=True)
timeout_retry.WaitFor(battery_updates_disabled, wait_period=1)
# TODO(rnephew): Remove when battery_utils is switched to.
@decorators.WithTimeoutAndRetriesFromInstance()
def EnableBatteryUpdates(self, timeout=None, retries=None):
""" Restarts device charging so that dumpsys no longer collects power data.
Args:
timeout: timeout in seconds
retries: number of retries
"""
def battery_updates_enabled():
return self.GetCharging() is True
self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'],
check_return=True)
self.RunShellCommand(['dumpsys', 'battery', 'reset'], check_return=True)
timeout_retry.WaitFor(battery_updates_enabled, wait_period=1)
# TODO(rnephew): Remove when battery_utils is switched to.
@contextlib.contextmanager
def BatteryMeasurement(self, timeout=None, retries=None):
"""Context manager that enables battery data collection. It makes
the device appear to stop charging so that dumpsys will start collecting
power data since last charge. Once the with block is exited, charging is
resumed and power data since last charge is no longer collected.
Only for devices L and higher.
Example usage:
with BatteryMeasurement():
browser_actions()
get_power_data() # report usage within this block
after_measurements() # Anything that runs after power
# measurements are collected
Args:
timeout: timeout in seconds
retries: number of retries
"""
if self.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP:
raise device_errors.CommandFailedError('Device must be L or higher.')
try:
self.DisableBatteryUpdates(timeout=timeout, retries=retries)
yield
finally:
self.EnableBatteryUpdates(timeout=timeout, retries=retries)
@decorators.WithTimeoutAndRetriesFromInstance() @decorators.WithTimeoutAndRetriesFromInstance()
def GetDevicePieWrapper(self, timeout=None, retries=None): def GetDevicePieWrapper(self, timeout=None, retries=None):
"""Gets the absolute path to the run_pie wrapper on the device. """Gets the absolute path to the run_pie wrapper on the device.
......
...@@ -1451,116 +1451,6 @@ class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsTest): ...@@ -1451,116 +1451,6 @@ class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsTest):
self.device.GetMemoryUsageForPid(4321)) self.device.GetMemoryUsageForPid(4321))
class DeviceUtilsGetBatteryInfoTest(DeviceUtilsTest):
def testGetBatteryInfo_normal(self):
with self.assertCall(
self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True),
[
'Current Battery Service state:',
' AC powered: false',
' USB powered: true',
' level: 100',
' temperature: 321',
]):
self.assertEquals(
{
'AC powered': 'false',
'USB powered': 'true',
'level': '100',
'temperature': '321',
},
self.device.GetBatteryInfo())
def testGetBatteryInfo_nothing(self):
with self.assertCall(
self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), []):
self.assertEquals({}, self.device.GetBatteryInfo())
class DeviceUtilsGetChargingTest(DeviceUtilsTest):
def testGetCharging_usb(self):
with self.assertCall(
self.call.device.GetBatteryInfo(), {'USB powered': 'true'}):
self.assertTrue(self.device.GetCharging())
def testGetCharging_usbFalse(self):
with self.assertCall(
self.call.device.GetBatteryInfo(), {'USB powered': 'false'}):
self.assertFalse(self.device.GetCharging())
def testGetCharging_ac(self):
with self.assertCall(
self.call.device.GetBatteryInfo(), {'AC powered': 'true'}):
self.assertTrue(self.device.GetCharging())
def testGetCharging_wireless(self):
with self.assertCall(
self.call.device.GetBatteryInfo(), {'Wireless powered': 'true'}):
self.assertTrue(self.device.GetCharging())
def testGetCharging_unknown(self):
with self.assertCall(
self.call.device.GetBatteryInfo(), {'level': '42'}):
self.assertFalse(self.device.GetCharging())
class DeviceUtilsSetChargingTest(DeviceUtilsTest):
@mock.patch('time.sleep', mock.Mock())
def testSetCharging_enabled(self):
with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), False),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True)):
self.device.SetCharging(True)
def testSetCharging_alreadyEnabled(self):
with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True)):
self.device.SetCharging(True)
@mock.patch('time.sleep', mock.Mock())
def testSetCharging_disabled(self):
with self.assertCalls(
(self.call.device.FileExists(mock.ANY), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), True),
(self.call.device.RunShellCommand(mock.ANY, check_return=True), []),
(self.call.device.GetCharging(), False)):
self.device.SetCharging(False)
class DeviceUtilsSetBatteryMeasurementTest(DeviceUtilsTest):
def testBatteryMeasurement(self):
with self.assertCalls(
(self.call.device.RunShellCommand(
mock.ANY, retries=0, single_line=True,
timeout=10, check_return=True), '22'),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '--checkin'],
check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
(self.call.device.GetCharging(), False),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'reset'], check_return=True), []),
(self.call.device.GetCharging(), True)):
with self.device.BatteryMeasurement():
pass
class DeviceUtilsStrTest(DeviceUtilsTest): class DeviceUtilsStrTest(DeviceUtilsTest):
def testStr_returnsSerial(self): def testStr_returnsSerial(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment