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