OLD | NEW |
(Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Provides a variety of device interactions with power. |
| 6 """ |
| 7 # pylint: disable=unused-argument |
| 8 |
| 9 import collections |
| 10 import contextlib |
| 11 import csv |
| 12 import logging |
| 13 |
| 14 from pylib import constants |
| 15 from pylib.device import decorators |
| 16 from pylib.device import device_errors |
| 17 from pylib.device import device_utils |
| 18 from pylib.utils import timeout_retry |
| 19 |
| 20 _DEFAULT_TIMEOUT = 30 |
| 21 _DEFAULT_RETRIES = 3 |
| 22 |
| 23 |
| 24 _DEVICE_PROFILES = [ |
| 25 { |
| 26 'name': 'Nexus 4', |
| 27 'witness_file': '/sys/module/pm8921_charger/parameters/disabled', |
| 28 'enable_command': ( |
| 29 'echo 0 > /sys/module/pm8921_charger/parameters/disabled && ' |
| 30 'dumpsys battery reset'), |
| 31 'disable_command': ( |
| 32 'echo 1 > /sys/module/pm8921_charger/parameters/disabled && ' |
| 33 'dumpsys battery set ac 0 && dumpsys battery set usb 0'), |
| 34 'charge_counter': None, |
| 35 'voltage': None, |
| 36 'current': None, |
| 37 }, |
| 38 { |
| 39 'name': 'Nexus 5', |
| 40 # Nexus 5 |
| 41 # Setting the HIZ bit of the bq24192 causes the charger to actually ignore |
| 42 # energy coming from USB. Setting the power_supply offline just updates the |
| 43 # Android system to reflect that. |
| 44 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT', |
| 45 'enable_command': ( |
| 46 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
| 47 'echo 1 > /sys/class/power_supply/usb/online &&' |
| 48 'dumpsys battery reset'), |
| 49 'disable_command': ( |
| 50 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
| 51 'chmod 644 /sys/class/power_supply/usb/online && ' |
| 52 'echo 0 > /sys/class/power_supply/usb/online && ' |
| 53 'dumpsys battery set ac 0 && dumpsys battery set usb 0'), |
| 54 'charge_counter': None, |
| 55 'voltage': None, |
| 56 'current': None, |
| 57 }, |
| 58 { |
| 59 'name': 'Nexus 6', |
| 60 'witness_file': None, |
| 61 'enable_command': ( |
| 62 'echo 1 > /sys/class/power_supply/battery/charging_enabled && ' |
| 63 'dumpsys battery reset'), |
| 64 'disable_command': ( |
| 65 'echo 0 > /sys/class/power_supply/battery/charging_enabled && ' |
| 66 'dumpsys battery set ac 0 && dumpsys battery set usb 0'), |
| 67 'charge_counter': ( |
| 68 '/sys/class/power_supply/max170xx_battery/charge_counter_ext'), |
| 69 'voltage': '/sys/class/power_supply/max170xx_battery/voltage_now', |
| 70 'current': '/sys/class/power_supply/max170xx_battery/current_now', |
| 71 }, |
| 72 { |
| 73 'name': 'Nexus 9', |
| 74 'witness_file': None, |
| 75 'enable_command': ( |
| 76 'echo Disconnected > ' |
| 77 '/sys/bus/i2c/drivers/bq2419x/0-006b/input_cable_state && ' |
| 78 'dumpsys battery reset'), |
| 79 'disable_command': ( |
| 80 'echo Connected > ' |
| 81 '/sys/bus/i2c/drivers/bq2419x/0-006b/input_cable_state && ' |
| 82 'dumpsys battery set ac 0 && dumpsys battery set usb 0'), |
| 83 'charge_counter': '/sys/class/power_supply/battery/charge_counter_ext', |
| 84 'voltage': '/sys/class/power_supply/battery/voltage_now', |
| 85 'current': '/sys/class/power_supply/battery/current_now', |
| 86 }, |
| 87 { |
| 88 'name': 'Nexus 10', |
| 89 'witness_file': None, |
| 90 'enable_command': None, |
| 91 'disable_command': None, |
| 92 'charge_counter': None, |
| 93 'voltage': '/sys/class/power_supply/ds2784-fuelgauge/voltage_now', |
| 94 'current': '/sys/class/power_supply/ds2784-fuelgauge/current_now', |
| 95 |
| 96 }, |
| 97 ] |
| 98 |
| 99 # The list of useful dumpsys columns. |
| 100 # Index of the column containing the format version. |
| 101 _DUMP_VERSION_INDEX = 0 |
| 102 # Index of the column containing the type of the row. |
| 103 _ROW_TYPE_INDEX = 3 |
| 104 # Index of the column containing the uid. |
| 105 _PACKAGE_UID_INDEX = 4 |
| 106 # Index of the column containing the application package. |
| 107 _PACKAGE_NAME_INDEX = 5 |
| 108 # The column containing the uid of the power data. |
| 109 _PWI_UID_INDEX = 1 |
| 110 # The column containing the type of consumption. Only consumtion since last |
| 111 # charge are of interest here. |
| 112 _PWI_AGGREGATION_INDEX = 2 |
| 113 # The column containing the amount of power used, in mah. |
| 114 _PWI_POWER_CONSUMPTION_INDEX = 5 |
| 115 |
| 116 |
| 117 class BatteryUtils(object): |
| 118 |
| 119 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
| 120 default_retries=_DEFAULT_RETRIES): |
| 121 """BatteryUtils constructor. |
| 122 |
| 123 Args: |
| 124 device: A DeviceUtils instance. |
| 125 default_timeout: An integer containing the default number of seconds to |
| 126 wait for an operation to complete if no explicit value |
| 127 is provided. |
| 128 default_retries: An integer containing the default number or times an |
| 129 operation should be retried on failure if no explicit |
| 130 value is provided. |
| 131 |
| 132 Raises: |
| 133 TypeError: If it is not passed a DeviceUtils instance. |
| 134 """ |
| 135 if not isinstance(device, device_utils.DeviceUtils): |
| 136 raise TypeError('Must be initialized with DeviceUtils object.') |
| 137 self._device = device |
| 138 self._cache = device.GetClientCache(self.__class__.__name__) |
| 139 self._default_timeout = default_timeout |
| 140 self._default_retries = default_retries |
| 141 |
| 142 @decorators.WithTimeoutAndRetriesFromInstance() |
| 143 def SupportsFuelGauge(self, timeout=None, retries=None): |
| 144 """Detect if fuel gauge chip is present. |
| 145 |
| 146 Args: |
| 147 timeout: timeout in seconds |
| 148 retries: number of retries |
| 149 |
| 150 Returns: |
| 151 True if known fuel gauge files are present. |
| 152 False otherwise. |
| 153 """ |
| 154 self._DiscoverDeviceProfile() |
| 155 return (self._cache['profile']['enable_command'] != None |
| 156 and self._cache['profile']['charge_counter'] != None) |
| 157 |
| 158 @decorators.WithTimeoutAndRetriesFromInstance() |
| 159 def GetFuelGaugeChargeCounter(self, timeout=None, retries=None): |
| 160 """Get value of charge_counter on fuel gauge chip. |
| 161 |
| 162 Device must have charging disabled for this, not just battery updates |
| 163 disabled. The only device that this currently works with is the nexus 5. |
| 164 |
| 165 Args: |
| 166 timeout: timeout in seconds |
| 167 retries: number of retries |
| 168 |
| 169 Returns: |
| 170 value of charge_counter for fuel gauge chip in units of nAh. |
| 171 |
| 172 Raises: |
| 173 device_errors.CommandFailedError: If fuel gauge chip not found. |
| 174 """ |
| 175 if self.SupportsFuelGauge(): |
| 176 return int(self._device.ReadFile( |
| 177 self._cache['profile']['charge_counter'])) |
| 178 raise device_errors.CommandFailedError( |
| 179 'Unable to find fuel gauge.') |
| 180 |
| 181 @decorators.WithTimeoutAndRetriesFromInstance() |
| 182 def GetNetworkData(self, package, timeout=None, retries=None): |
| 183 """Get network data for specific package. |
| 184 |
| 185 Args: |
| 186 package: package name you want network data for. |
| 187 timeout: timeout in seconds |
| 188 retries: number of retries |
| 189 |
| 190 Returns: |
| 191 Tuple of (sent_data, recieved_data) |
| 192 None if no network data found |
| 193 """ |
| 194 # If device_utils clears cache, cache['uids'] doesn't exist |
| 195 if 'uids' not in self._cache: |
| 196 self._cache['uids'] = {} |
| 197 if package not in self._cache['uids']: |
| 198 self.GetPowerData() |
| 199 if package not in self._cache['uids']: |
| 200 logging.warning('No UID found for %s. Can\'t get network data.', |
| 201 package) |
| 202 return None |
| 203 |
| 204 network_data_path = '/proc/uid_stat/%s/' % self._cache['uids'][package] |
| 205 try: |
| 206 send_data = int(self._device.ReadFile(network_data_path + 'tcp_snd')) |
| 207 # If ReadFile throws exception, it means no network data usage file for |
| 208 # package has been recorded. Return 0 sent and 0 received. |
| 209 except device_errors.AdbShellCommandFailedError: |
| 210 logging.warning('No sent data found for package %s', package) |
| 211 send_data = 0 |
| 212 try: |
| 213 recv_data = int(self._device.ReadFile(network_data_path + 'tcp_rcv')) |
| 214 except device_errors.AdbShellCommandFailedError: |
| 215 logging.warning('No received data found for package %s', package) |
| 216 recv_data = 0 |
| 217 return (send_data, recv_data) |
| 218 |
| 219 @decorators.WithTimeoutAndRetriesFromInstance() |
| 220 def GetPowerData(self, timeout=None, retries=None): |
| 221 """Get power data for device. |
| 222 |
| 223 Args: |
| 224 timeout: timeout in seconds |
| 225 retries: number of retries |
| 226 |
| 227 Returns: |
| 228 Dict of power data, keyed on package names. |
| 229 { |
| 230 package_name: { |
| 231 'uid': uid, |
| 232 'data': [1,2,3] |
| 233 }, |
| 234 } |
| 235 """ |
| 236 if 'uids' not in self._cache: |
| 237 self._cache['uids'] = {} |
| 238 dumpsys_output = self._device.RunShellCommand( |
| 239 ['dumpsys', 'batterystats', '-c'], check_return=True) |
| 240 csvreader = csv.reader(dumpsys_output) |
| 241 pwi_entries = collections.defaultdict(list) |
| 242 for entry in csvreader: |
| 243 if entry[_DUMP_VERSION_INDEX] not in ['8', '9']: |
| 244 # Wrong dumpsys version. |
| 245 raise device_errors.DeviceVersionError( |
| 246 'Dumpsys version must be 8 or 9. %s found.' |
| 247 % entry[_DUMP_VERSION_INDEX]) |
| 248 if _ROW_TYPE_INDEX < len(entry) and entry[_ROW_TYPE_INDEX] == 'uid': |
| 249 current_package = entry[_PACKAGE_NAME_INDEX] |
| 250 if (self._cache['uids'].get(current_package) |
| 251 and self._cache['uids'].get(current_package) |
| 252 != entry[_PACKAGE_UID_INDEX]): |
| 253 raise device_errors.CommandFailedError( |
| 254 'Package %s found multiple times with differnt UIDs %s and %s' |
| 255 % (current_package, self._cache['uids'][current_package], |
| 256 entry[_PACKAGE_UID_INDEX])) |
| 257 self._cache['uids'][current_package] = entry[_PACKAGE_UID_INDEX] |
| 258 elif (_PWI_POWER_CONSUMPTION_INDEX < len(entry) |
| 259 and entry[_ROW_TYPE_INDEX] == 'pwi' |
| 260 and entry[_PWI_AGGREGATION_INDEX] == 'l'): |
| 261 pwi_entries[entry[_PWI_UID_INDEX]].append( |
| 262 float(entry[_PWI_POWER_CONSUMPTION_INDEX])) |
| 263 |
| 264 return {p: {'uid': uid, 'data': pwi_entries[uid]} |
| 265 for p, uid in self._cache['uids'].iteritems()} |
| 266 |
| 267 @decorators.WithTimeoutAndRetriesFromInstance() |
| 268 def GetPackagePowerData(self, package, timeout=None, retries=None): |
| 269 """Get power data for particular package. |
| 270 |
| 271 Args: |
| 272 package: Package to get power data on. |
| 273 |
| 274 returns: |
| 275 Dict of UID and power data. |
| 276 { |
| 277 'uid': uid, |
| 278 'data': [1,2,3] |
| 279 } |
| 280 None if the package is not found in the power data. |
| 281 """ |
| 282 return self.GetPowerData().get(package) |
| 283 |
| 284 @decorators.WithTimeoutAndRetriesFromInstance() |
| 285 def GetBatteryInfo(self, timeout=None, retries=None): |
| 286 """Gets battery info for the device. |
| 287 |
| 288 Args: |
| 289 timeout: timeout in seconds |
| 290 retries: number of retries |
| 291 Returns: |
| 292 A dict containing various battery information as reported by dumpsys |
| 293 battery. |
| 294 """ |
| 295 result = {} |
| 296 # Skip the first line, which is just a header. |
| 297 for line in self._device.RunShellCommand( |
| 298 ['dumpsys', 'battery'], check_return=True)[1:]: |
| 299 # If usb charging has been disabled, an extra line of header exists. |
| 300 if 'UPDATES STOPPED' in line: |
| 301 logging.warning('Dumpsys battery not receiving updates. ' |
| 302 'Run dumpsys battery reset if this is in error.') |
| 303 elif ':' not in line: |
| 304 logging.warning('Unknown line found in dumpsys battery: "%s"', line) |
| 305 else: |
| 306 k, v = line.split(':', 1) |
| 307 result[k.strip()] = v.strip() |
| 308 return result |
| 309 |
| 310 @decorators.WithTimeoutAndRetriesFromInstance() |
| 311 def GetCharging(self, timeout=None, retries=None): |
| 312 """Gets the charging state of the device. |
| 313 |
| 314 Args: |
| 315 timeout: timeout in seconds |
| 316 retries: number of retries |
| 317 Returns: |
| 318 True if the device is charging, false otherwise. |
| 319 """ |
| 320 battery_info = self.GetBatteryInfo() |
| 321 for k in ('AC powered', 'USB powered', 'Wireless powered'): |
| 322 if (k in battery_info and |
| 323 battery_info[k].lower() in ('true', '1', 'yes')): |
| 324 return True |
| 325 return False |
| 326 |
| 327 @decorators.WithTimeoutAndRetriesFromInstance() |
| 328 def SetCharging(self, enabled, timeout=None, retries=None): |
| 329 """Enables or disables charging on the device. |
| 330 |
| 331 Args: |
| 332 enabled: A boolean indicating whether charging should be enabled or |
| 333 disabled. |
| 334 timeout: timeout in seconds |
| 335 retries: number of retries |
| 336 |
| 337 Raises: |
| 338 device_errors.CommandFailedError: If method of disabling charging cannot |
| 339 be determined. |
| 340 """ |
| 341 self._DiscoverDeviceProfile() |
| 342 if not self._cache['profile']['enable_command']: |
| 343 raise device_errors.CommandFailedError( |
| 344 'Unable to find charging commands.') |
| 345 |
| 346 if enabled: |
| 347 command = self._cache['profile']['enable_command'] |
| 348 else: |
| 349 command = self._cache['profile']['disable_command'] |
| 350 |
| 351 def set_and_verify_charging(): |
| 352 self._device.RunShellCommand(command, check_return=True) |
| 353 return self.GetCharging() == enabled |
| 354 |
| 355 timeout_retry.WaitFor(set_and_verify_charging, wait_period=1) |
| 356 |
| 357 # TODO(rnephew): Make private when all use cases can use the context manager. |
| 358 @decorators.WithTimeoutAndRetriesFromInstance() |
| 359 def DisableBatteryUpdates(self, timeout=None, retries=None): |
| 360 """Resets battery data and makes device appear like it is not |
| 361 charging so that it will collect power data since last charge. |
| 362 |
| 363 Args: |
| 364 timeout: timeout in seconds |
| 365 retries: number of retries |
| 366 |
| 367 Raises: |
| 368 device_errors.CommandFailedError: When resetting batterystats fails to |
| 369 reset power values. |
| 370 device_errors.DeviceVersionError: If device is not L or higher. |
| 371 """ |
| 372 def battery_updates_disabled(): |
| 373 return self.GetCharging() is False |
| 374 |
| 375 self._ClearPowerData() |
| 376 self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'ac', '0'], |
| 377 check_return=True) |
| 378 self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'], |
| 379 check_return=True) |
| 380 timeout_retry.WaitFor(battery_updates_disabled, wait_period=1) |
| 381 |
| 382 # TODO(rnephew): Make private when all use cases can use the context manager. |
| 383 @decorators.WithTimeoutAndRetriesFromInstance() |
| 384 def EnableBatteryUpdates(self, timeout=None, retries=None): |
| 385 """Restarts device charging so that dumpsys no longer collects power data. |
| 386 |
| 387 Args: |
| 388 timeout: timeout in seconds |
| 389 retries: number of retries |
| 390 |
| 391 Raises: |
| 392 device_errors.DeviceVersionError: If device is not L or higher. |
| 393 """ |
| 394 def battery_updates_enabled(): |
| 395 return (self.GetCharging() |
| 396 or not bool('UPDATES STOPPED' in self._device.RunShellCommand( |
| 397 ['dumpsys', 'battery'], check_return=True))) |
| 398 |
| 399 self._device.RunShellCommand(['dumpsys', 'battery', 'reset'], |
| 400 check_return=True) |
| 401 timeout_retry.WaitFor(battery_updates_enabled, wait_period=1) |
| 402 |
| 403 @contextlib.contextmanager |
| 404 def BatteryMeasurement(self, timeout=None, retries=None): |
| 405 """Context manager that enables battery data collection. It makes |
| 406 the device appear to stop charging so that dumpsys will start collecting |
| 407 power data since last charge. Once the with block is exited, charging is |
| 408 resumed and power data since last charge is no longer collected. |
| 409 |
| 410 Only for devices L and higher. |
| 411 |
| 412 Example usage: |
| 413 with BatteryMeasurement(): |
| 414 browser_actions() |
| 415 get_power_data() # report usage within this block |
| 416 after_measurements() # Anything that runs after power |
| 417 # measurements are collected |
| 418 |
| 419 Args: |
| 420 timeout: timeout in seconds |
| 421 retries: number of retries |
| 422 |
| 423 Raises: |
| 424 device_errors.DeviceVersionError: If device is not L or higher. |
| 425 """ |
| 426 if (self._device.build_version_sdk < |
| 427 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP): |
| 428 raise device_errors.DeviceVersionError('Device must be L or higher.') |
| 429 try: |
| 430 self.DisableBatteryUpdates(timeout=timeout, retries=retries) |
| 431 yield |
| 432 finally: |
| 433 self.EnableBatteryUpdates(timeout=timeout, retries=retries) |
| 434 |
| 435 def ChargeDeviceToLevel(self, level, wait_period=60): |
| 436 """Enables charging and waits for device to be charged to given level. |
| 437 |
| 438 Args: |
| 439 level: level of charge to wait for. |
| 440 wait_period: time in seconds to wait between checking. |
| 441 """ |
| 442 self.SetCharging(True) |
| 443 |
| 444 def device_charged(): |
| 445 battery_level = self.GetBatteryInfo().get('level') |
| 446 if battery_level is None: |
| 447 logging.warning('Unable to find current battery level.') |
| 448 battery_level = 100 |
| 449 else: |
| 450 logging.info('current battery level: %s', battery_level) |
| 451 battery_level = int(battery_level) |
| 452 return battery_level >= level |
| 453 |
| 454 timeout_retry.WaitFor(device_charged, wait_period=wait_period) |
| 455 |
| 456 def LetBatteryCoolToTemperature(self, target_temp, wait_period=60): |
| 457 """Lets device sit to give battery time to cool down |
| 458 Args: |
| 459 temp: maximum temperature to allow in tenths of degrees c. |
| 460 wait_period: time in seconds to wait between checking. |
| 461 """ |
| 462 def cool_device(): |
| 463 temp = self.GetBatteryInfo().get('temperature') |
| 464 if temp is None: |
| 465 logging.warning('Unable to find current battery temperature.') |
| 466 temp = 0 |
| 467 else: |
| 468 logging.info('Current battery temperature: %s', temp) |
| 469 return int(temp) <= target_temp |
| 470 self.EnableBatteryUpdates() |
| 471 logging.info('Waiting for the device to cool down to %s (0.1 C)', |
| 472 target_temp) |
| 473 timeout_retry.WaitFor(cool_device, wait_period=wait_period) |
| 474 |
| 475 @decorators.WithTimeoutAndRetriesFromInstance() |
| 476 def TieredSetCharging(self, enabled, timeout=None, retries=None): |
| 477 """Enables or disables charging on the device. |
| 478 |
| 479 Args: |
| 480 enabled: A boolean indicating whether charging should be enabled or |
| 481 disabled. |
| 482 timeout: timeout in seconds |
| 483 retries: number of retries |
| 484 """ |
| 485 if self.GetCharging() == enabled: |
| 486 logging.warning('Device charging already in expected state: %s', enabled) |
| 487 return |
| 488 |
| 489 if enabled: |
| 490 try: |
| 491 self.SetCharging(enabled) |
| 492 except device_errors.CommandFailedError: |
| 493 logging.info('Unable to enable charging via hardware.' |
| 494 ' Falling back to software enabling.') |
| 495 self.EnableBatteryUpdates() |
| 496 else: |
| 497 try: |
| 498 self._ClearPowerData() |
| 499 self.SetCharging(enabled) |
| 500 except device_errors.CommandFailedError: |
| 501 logging.info('Unable to disable charging via hardware.' |
| 502 ' Falling back to software disabling.') |
| 503 self.DisableBatteryUpdates() |
| 504 |
| 505 @contextlib.contextmanager |
| 506 def PowerMeasurement(self, timeout=None, retries=None): |
| 507 """Context manager that enables battery power collection. |
| 508 |
| 509 Once the with block is exited, charging is resumed. Will attempt to disable |
| 510 charging at the hardware level, and if that fails will fall back to software |
| 511 disabling of battery updates. |
| 512 |
| 513 Only for devices L and higher. |
| 514 |
| 515 Example usage: |
| 516 with PowerMeasurement(): |
| 517 browser_actions() |
| 518 get_power_data() # report usage within this block |
| 519 after_measurements() # Anything that runs after power |
| 520 # measurements are collected |
| 521 |
| 522 Args: |
| 523 timeout: timeout in seconds |
| 524 retries: number of retries |
| 525 """ |
| 526 try: |
| 527 self.TieredSetCharging(False, timeout=timeout, retries=retries) |
| 528 yield |
| 529 finally: |
| 530 self.TieredSetCharging(True, timeout=timeout, retries=retries) |
| 531 |
| 532 def _ClearPowerData(self): |
| 533 """Resets battery data and makes device appear like it is not |
| 534 charging so that it will collect power data since last charge. |
| 535 |
| 536 Returns: |
| 537 True if power data cleared. |
| 538 False if power data clearing is not supported (pre-L) |
| 539 |
| 540 Raises: |
| 541 device_errors.DeviceVersionError: If power clearing is supported, |
| 542 but fails. |
| 543 """ |
| 544 if (self._device.build_version_sdk < |
| 545 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP): |
| 546 logging.warning('Dumpsys power data only available on 5.0 and above. ' |
| 547 'Cannot clear power data.') |
| 548 return False |
| 549 |
| 550 self._device.RunShellCommand( |
| 551 ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True) |
| 552 self._device.RunShellCommand( |
| 553 ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True) |
| 554 self._device.RunShellCommand( |
| 555 ['dumpsys', 'batterystats', '--reset'], check_return=True) |
| 556 battery_data = self._device.RunShellCommand( |
| 557 ['dumpsys', 'batterystats', '--charged', '--checkin'], |
| 558 check_return=True, large_output=True) |
| 559 for line in battery_data: |
| 560 l = line.split(',') |
| 561 if (len(l) > _PWI_POWER_CONSUMPTION_INDEX and l[_ROW_TYPE_INDEX] == 'pwi' |
| 562 and l[_PWI_POWER_CONSUMPTION_INDEX] != 0): |
| 563 self._device.RunShellCommand( |
| 564 ['dumpsys', 'battery', 'reset'], check_return=True) |
| 565 raise device_errors.CommandFailedError( |
| 566 'Non-zero pmi value found after reset.') |
| 567 self._device.RunShellCommand( |
| 568 ['dumpsys', 'battery', 'reset'], check_return=True) |
| 569 return True |
| 570 |
| 571 def _DiscoverDeviceProfile(self): |
| 572 """Checks and caches device information. |
| 573 |
| 574 Returns: |
| 575 True if profile is found, false otherwise. |
| 576 """ |
| 577 |
| 578 if 'profile' in self._cache: |
| 579 return True |
| 580 for profile in _DEVICE_PROFILES: |
| 581 if self._device.product_model == profile['name']: |
| 582 self._cache['profile'] = profile |
| 583 return True |
| 584 self._cache['profile'] = { |
| 585 'name': None, |
| 586 'witness_file': None, |
| 587 'enable_command': None, |
| 588 'disable_command': None, |
| 589 'charge_counter': None, |
| 590 'voltage': None, |
| 591 'current': None, |
| 592 } |
| 593 return False |
OLD | NEW |