OLD | NEW |
(Empty) | |
| 1 # Copyright 2014 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 based on adb. |
| 6 |
| 7 Eventually, this will be based on adb_wrapper. |
| 8 """ |
| 9 # pylint: disable=unused-argument |
| 10 |
| 11 import collections |
| 12 import contextlib |
| 13 import itertools |
| 14 import logging |
| 15 import multiprocessing |
| 16 import os |
| 17 import posixpath |
| 18 import re |
| 19 import shutil |
| 20 import sys |
| 21 import tempfile |
| 22 import time |
| 23 import zipfile |
| 24 |
| 25 import pylib.android_commands |
| 26 from pylib import cmd_helper |
| 27 from pylib import constants |
| 28 from pylib import device_signal |
| 29 from pylib.constants import keyevent |
| 30 from pylib.device import adb_wrapper |
| 31 from pylib.device import decorators |
| 32 from pylib.device import device_blacklist |
| 33 from pylib.device import device_errors |
| 34 from pylib.device import intent |
| 35 from pylib.device import logcat_monitor |
| 36 from pylib.device.commands import install_commands |
| 37 from pylib.sdk import split_select |
| 38 from pylib.utils import apk_helper |
| 39 from pylib.utils import base_error |
| 40 from pylib.utils import device_temp_file |
| 41 from pylib.utils import host_utils |
| 42 from pylib.utils import md5sum |
| 43 from pylib.utils import parallelizer |
| 44 from pylib.utils import timeout_retry |
| 45 from pylib.utils import zip_utils |
| 46 |
| 47 _DEFAULT_TIMEOUT = 30 |
| 48 _DEFAULT_RETRIES = 3 |
| 49 |
| 50 # A sentinel object for default values |
| 51 # TODO(jbudorick,perezju): revisit how default values are handled by |
| 52 # the timeout_retry decorators. |
| 53 DEFAULT = object() |
| 54 |
| 55 _CONTROL_CHARGING_COMMANDS = [ |
| 56 { |
| 57 # Nexus 4 |
| 58 'witness_file': '/sys/module/pm8921_charger/parameters/disabled', |
| 59 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled', |
| 60 'disable_command': |
| 61 'echo 1 > /sys/module/pm8921_charger/parameters/disabled', |
| 62 }, |
| 63 { |
| 64 # Nexus 5 |
| 65 # Setting the HIZ bit of the bq24192 causes the charger to actually ignore |
| 66 # energy coming from USB. Setting the power_supply offline just updates the |
| 67 # Android system to reflect that. |
| 68 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT', |
| 69 'enable_command': ( |
| 70 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
| 71 'echo 1 > /sys/class/power_supply/usb/online'), |
| 72 'disable_command': ( |
| 73 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
| 74 'chmod 644 /sys/class/power_supply/usb/online && ' |
| 75 'echo 0 > /sys/class/power_supply/usb/online'), |
| 76 }, |
| 77 ] |
| 78 |
| 79 |
| 80 @decorators.WithExplicitTimeoutAndRetries( |
| 81 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 82 def GetAVDs(): |
| 83 """Returns a list of Android Virtual Devices. |
| 84 |
| 85 Returns: |
| 86 A list containing the configured AVDs. |
| 87 """ |
| 88 lines = cmd_helper.GetCmdOutput([ |
| 89 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'), |
| 90 'list', 'avd']).splitlines() |
| 91 avds = [] |
| 92 for line in lines: |
| 93 if 'Name:' not in line: |
| 94 continue |
| 95 key, value = (s.strip() for s in line.split(':', 1)) |
| 96 if key == 'Name': |
| 97 avds.append(value) |
| 98 return avds |
| 99 |
| 100 |
| 101 @decorators.WithExplicitTimeoutAndRetries( |
| 102 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 103 def RestartServer(): |
| 104 """Restarts the adb server. |
| 105 |
| 106 Raises: |
| 107 CommandFailedError if we fail to kill or restart the server. |
| 108 """ |
| 109 def adb_killed(): |
| 110 return not adb_wrapper.AdbWrapper.IsServerOnline() |
| 111 |
| 112 def adb_started(): |
| 113 return adb_wrapper.AdbWrapper.IsServerOnline() |
| 114 |
| 115 adb_wrapper.AdbWrapper.KillServer() |
| 116 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5): |
| 117 # TODO(perezju): raise an exception after fixng http://crbug.com/442319 |
| 118 logging.warning('Failed to kill adb server') |
| 119 adb_wrapper.AdbWrapper.StartServer() |
| 120 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5): |
| 121 raise device_errors.CommandFailedError('Failed to start adb server') |
| 122 |
| 123 |
| 124 def _GetTimeStamp(): |
| 125 """Return a basic ISO 8601 time stamp with the current local time.""" |
| 126 return time.strftime('%Y%m%dT%H%M%S', time.localtime()) |
| 127 |
| 128 |
| 129 def _JoinLines(lines): |
| 130 # makes sure that the last line is also terminated, and is more memory |
| 131 # efficient than first appending an end-line to each line and then joining |
| 132 # all of them together. |
| 133 return ''.join(s for line in lines for s in (line, '\n')) |
| 134 |
| 135 |
| 136 class DeviceUtils(object): |
| 137 |
| 138 _MAX_ADB_COMMAND_LENGTH = 512 |
| 139 _MAX_ADB_OUTPUT_LENGTH = 32768 |
| 140 _LAUNCHER_FOCUSED_RE = re.compile( |
| 141 '\s*mCurrentFocus.*(Launcher|launcher).*') |
| 142 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
| 143 |
| 144 # Property in /data/local.prop that controls Java assertions. |
| 145 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' |
| 146 |
| 147 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
| 148 default_retries=_DEFAULT_RETRIES): |
| 149 """DeviceUtils constructor. |
| 150 |
| 151 Args: |
| 152 device: Either a device serial, an existing AdbWrapper instance, or an |
| 153 an existing AndroidCommands instance. |
| 154 default_timeout: An integer containing the default number of seconds to |
| 155 wait for an operation to complete if no explicit value |
| 156 is provided. |
| 157 default_retries: An integer containing the default number or times an |
| 158 operation should be retried on failure if no explicit |
| 159 value is provided. |
| 160 """ |
| 161 self.adb = None |
| 162 self.old_interface = None |
| 163 if isinstance(device, basestring): |
| 164 self.adb = adb_wrapper.AdbWrapper(device) |
| 165 self.old_interface = pylib.android_commands.AndroidCommands(device) |
| 166 elif isinstance(device, adb_wrapper.AdbWrapper): |
| 167 self.adb = device |
| 168 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
| 169 elif isinstance(device, pylib.android_commands.AndroidCommands): |
| 170 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) |
| 171 self.old_interface = device |
| 172 else: |
| 173 raise ValueError('Unsupported device value: %r' % device) |
| 174 self._commands_installed = None |
| 175 self._default_timeout = default_timeout |
| 176 self._default_retries = default_retries |
| 177 self._cache = {} |
| 178 self._client_caches = {} |
| 179 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) |
| 180 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) |
| 181 |
| 182 def __eq__(self, other): |
| 183 """Checks whether |other| refers to the same device as |self|. |
| 184 |
| 185 Args: |
| 186 other: The object to compare to. This can be a basestring, an instance |
| 187 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. |
| 188 Returns: |
| 189 Whether |other| refers to the same device as |self|. |
| 190 """ |
| 191 return self.adb.GetDeviceSerial() == str(other) |
| 192 |
| 193 def __lt__(self, other): |
| 194 """Compares two instances of DeviceUtils. |
| 195 |
| 196 This merely compares their serial numbers. |
| 197 |
| 198 Args: |
| 199 other: The instance of DeviceUtils to compare to. |
| 200 Returns: |
| 201 Whether |self| is less than |other|. |
| 202 """ |
| 203 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial() |
| 204 |
| 205 def __str__(self): |
| 206 """Returns the device serial.""" |
| 207 return self.adb.GetDeviceSerial() |
| 208 |
| 209 @decorators.WithTimeoutAndRetriesFromInstance() |
| 210 def IsOnline(self, timeout=None, retries=None): |
| 211 """Checks whether the device is online. |
| 212 |
| 213 Args: |
| 214 timeout: timeout in seconds |
| 215 retries: number of retries |
| 216 |
| 217 Returns: |
| 218 True if the device is online, False otherwise. |
| 219 |
| 220 Raises: |
| 221 CommandTimeoutError on timeout. |
| 222 """ |
| 223 try: |
| 224 return self.adb.GetState() == 'device' |
| 225 except base_error.BaseError as exc: |
| 226 logging.info('Failed to get state: %s', exc) |
| 227 return False |
| 228 |
| 229 @decorators.WithTimeoutAndRetriesFromInstance() |
| 230 def HasRoot(self, timeout=None, retries=None): |
| 231 """Checks whether or not adbd has root privileges. |
| 232 |
| 233 Args: |
| 234 timeout: timeout in seconds |
| 235 retries: number of retries |
| 236 |
| 237 Returns: |
| 238 True if adbd has root privileges, False otherwise. |
| 239 |
| 240 Raises: |
| 241 CommandTimeoutError on timeout. |
| 242 DeviceUnreachableError on missing device. |
| 243 """ |
| 244 try: |
| 245 self.RunShellCommand('ls /root', check_return=True) |
| 246 return True |
| 247 except device_errors.AdbCommandFailedError: |
| 248 return False |
| 249 |
| 250 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT): |
| 251 """Checks whether 'su' is needed to access protected resources. |
| 252 |
| 253 Args: |
| 254 timeout: timeout in seconds |
| 255 retries: number of retries |
| 256 |
| 257 Returns: |
| 258 True if 'su' is available on the device and is needed to to access |
| 259 protected resources; False otherwise if either 'su' is not available |
| 260 (e.g. because the device has a user build), or not needed (because adbd |
| 261 already has root privileges). |
| 262 |
| 263 Raises: |
| 264 CommandTimeoutError on timeout. |
| 265 DeviceUnreachableError on missing device. |
| 266 """ |
| 267 if 'needs_su' not in self._cache: |
| 268 try: |
| 269 self.RunShellCommand( |
| 270 'su -c ls /root && ! ls /root', check_return=True, |
| 271 timeout=self._default_timeout if timeout is DEFAULT else timeout, |
| 272 retries=self._default_retries if retries is DEFAULT else retries) |
| 273 self._cache['needs_su'] = True |
| 274 except device_errors.AdbCommandFailedError: |
| 275 self._cache['needs_su'] = False |
| 276 return self._cache['needs_su'] |
| 277 |
| 278 |
| 279 @decorators.WithTimeoutAndRetriesFromInstance() |
| 280 def EnableRoot(self, timeout=None, retries=None): |
| 281 """Restarts adbd with root privileges. |
| 282 |
| 283 Args: |
| 284 timeout: timeout in seconds |
| 285 retries: number of retries |
| 286 |
| 287 Raises: |
| 288 CommandFailedError if root could not be enabled. |
| 289 CommandTimeoutError on timeout. |
| 290 """ |
| 291 if self.IsUserBuild(): |
| 292 raise device_errors.CommandFailedError( |
| 293 'Cannot enable root in user builds.', str(self)) |
| 294 if 'needs_su' in self._cache: |
| 295 del self._cache['needs_su'] |
| 296 self.adb.Root() |
| 297 self.WaitUntilFullyBooted() |
| 298 |
| 299 @decorators.WithTimeoutAndRetriesFromInstance() |
| 300 def IsUserBuild(self, timeout=None, retries=None): |
| 301 """Checks whether or not the device is running a user build. |
| 302 |
| 303 Args: |
| 304 timeout: timeout in seconds |
| 305 retries: number of retries |
| 306 |
| 307 Returns: |
| 308 True if the device is running a user build, False otherwise (i.e. if |
| 309 it's running a userdebug build). |
| 310 |
| 311 Raises: |
| 312 CommandTimeoutError on timeout. |
| 313 DeviceUnreachableError on missing device. |
| 314 """ |
| 315 return self.build_type == 'user' |
| 316 |
| 317 @decorators.WithTimeoutAndRetriesFromInstance() |
| 318 def GetExternalStoragePath(self, timeout=None, retries=None): |
| 319 """Get the device's path to its SD card. |
| 320 |
| 321 Args: |
| 322 timeout: timeout in seconds |
| 323 retries: number of retries |
| 324 |
| 325 Returns: |
| 326 The device's path to its SD card. |
| 327 |
| 328 Raises: |
| 329 CommandFailedError if the external storage path could not be determined. |
| 330 CommandTimeoutError on timeout. |
| 331 DeviceUnreachableError on missing device. |
| 332 """ |
| 333 if 'external_storage' in self._cache: |
| 334 return self._cache['external_storage'] |
| 335 |
| 336 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', |
| 337 single_line=True, |
| 338 check_return=True) |
| 339 if not value: |
| 340 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
| 341 str(self)) |
| 342 self._cache['external_storage'] = value |
| 343 return value |
| 344 |
| 345 @decorators.WithTimeoutAndRetriesFromInstance() |
| 346 def GetApplicationPaths(self, package, timeout=None, retries=None): |
| 347 """Get the paths of the installed apks on the device for the given package. |
| 348 |
| 349 Args: |
| 350 package: Name of the package. |
| 351 |
| 352 Returns: |
| 353 List of paths to the apks on the device for the given package. |
| 354 """ |
| 355 # 'pm path' is liable to incorrectly exit with a nonzero number starting |
| 356 # in Lollipop. |
| 357 # TODO(jbudorick): Check if this is fixed as new Android versions are |
| 358 # released to put an upper bound on this. |
| 359 should_check_return = (self.build_version_sdk < |
| 360 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
| 361 output = self.RunShellCommand( |
| 362 ['pm', 'path', package], check_return=should_check_return) |
| 363 apks = [] |
| 364 for line in output: |
| 365 if not line.startswith('package:'): |
| 366 raise device_errors.CommandFailedError( |
| 367 'pm path returned: %r' % '\n'.join(output), str(self)) |
| 368 apks.append(line[len('package:'):]) |
| 369 return apks |
| 370 |
| 371 @decorators.WithTimeoutAndRetriesFromInstance() |
| 372 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): |
| 373 """Get the data directory on the device for the given package. |
| 374 |
| 375 Args: |
| 376 package: Name of the package. |
| 377 |
| 378 Returns: |
| 379 The package's data directory, or None if the package doesn't exist on the |
| 380 device. |
| 381 """ |
| 382 try: |
| 383 output = self._RunPipedShellCommand( |
| 384 'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package)) |
| 385 for line in output: |
| 386 _, _, dataDir = line.partition('dataDir=') |
| 387 if dataDir: |
| 388 return dataDir |
| 389 except device_errors.CommandFailedError: |
| 390 logging.exception('Could not find data directory for %s', package) |
| 391 return None |
| 392 |
| 393 @decorators.WithTimeoutAndRetriesFromInstance() |
| 394 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| 395 """Wait for the device to fully boot. |
| 396 |
| 397 This means waiting for the device to boot, the package manager to be |
| 398 available, and the SD card to be ready. It can optionally mean waiting |
| 399 for wifi to come up, too. |
| 400 |
| 401 Args: |
| 402 wifi: A boolean indicating if we should wait for wifi to come up or not. |
| 403 timeout: timeout in seconds |
| 404 retries: number of retries |
| 405 |
| 406 Raises: |
| 407 CommandFailedError on failure. |
| 408 CommandTimeoutError if one of the component waits times out. |
| 409 DeviceUnreachableError if the device becomes unresponsive. |
| 410 """ |
| 411 def sd_card_ready(): |
| 412 try: |
| 413 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], |
| 414 check_return=True) |
| 415 return True |
| 416 except device_errors.AdbCommandFailedError: |
| 417 return False |
| 418 |
| 419 def pm_ready(): |
| 420 try: |
| 421 return self.GetApplicationPaths('android') |
| 422 except device_errors.CommandFailedError: |
| 423 return False |
| 424 |
| 425 def boot_completed(): |
| 426 return self.GetProp('sys.boot_completed') == '1' |
| 427 |
| 428 def wifi_enabled(): |
| 429 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], |
| 430 check_return=False) |
| 431 |
| 432 self.adb.WaitForDevice() |
| 433 timeout_retry.WaitFor(sd_card_ready) |
| 434 timeout_retry.WaitFor(pm_ready) |
| 435 timeout_retry.WaitFor(boot_completed) |
| 436 if wifi: |
| 437 timeout_retry.WaitFor(wifi_enabled) |
| 438 |
| 439 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
| 440 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 441 |
| 442 @decorators.WithTimeoutAndRetriesDefaults( |
| 443 REBOOT_DEFAULT_TIMEOUT, |
| 444 REBOOT_DEFAULT_RETRIES) |
| 445 def Reboot(self, block=True, wifi=False, timeout=None, retries=None): |
| 446 """Reboot the device. |
| 447 |
| 448 Args: |
| 449 block: A boolean indicating if we should wait for the reboot to complete. |
| 450 wifi: A boolean indicating if we should wait for wifi to be enabled after |
| 451 the reboot. The option has no effect unless |block| is also True. |
| 452 timeout: timeout in seconds |
| 453 retries: number of retries |
| 454 |
| 455 Raises: |
| 456 CommandTimeoutError on timeout. |
| 457 DeviceUnreachableError on missing device. |
| 458 """ |
| 459 def device_offline(): |
| 460 return not self.IsOnline() |
| 461 |
| 462 self.adb.Reboot() |
| 463 self._ClearCache() |
| 464 timeout_retry.WaitFor(device_offline, wait_period=1) |
| 465 if block: |
| 466 self.WaitUntilFullyBooted(wifi=wifi) |
| 467 |
| 468 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT |
| 469 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 470 |
| 471 @decorators.WithTimeoutAndRetriesDefaults( |
| 472 INSTALL_DEFAULT_TIMEOUT, |
| 473 INSTALL_DEFAULT_RETRIES) |
| 474 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): |
| 475 """Install an APK. |
| 476 |
| 477 Noop if an identical APK is already installed. |
| 478 |
| 479 Args: |
| 480 apk_path: A string containing the path to the APK to install. |
| 481 reinstall: A boolean indicating if we should keep any existing app data. |
| 482 timeout: timeout in seconds |
| 483 retries: number of retries |
| 484 |
| 485 Raises: |
| 486 CommandFailedError if the installation fails. |
| 487 CommandTimeoutError if the installation times out. |
| 488 DeviceUnreachableError on missing device. |
| 489 """ |
| 490 package_name = apk_helper.GetPackageName(apk_path) |
| 491 device_paths = self.GetApplicationPaths(package_name) |
| 492 if device_paths: |
| 493 if len(device_paths) > 1: |
| 494 logging.warning( |
| 495 'Installing single APK (%s) when split APKs (%s) are currently ' |
| 496 'installed.', apk_path, ' '.join(device_paths)) |
| 497 (files_to_push, _) = self._GetChangedAndStaleFiles( |
| 498 apk_path, device_paths[0]) |
| 499 should_install = bool(files_to_push) |
| 500 if should_install and not reinstall: |
| 501 self.adb.Uninstall(package_name) |
| 502 else: |
| 503 should_install = True |
| 504 if should_install: |
| 505 self.adb.Install(apk_path, reinstall=reinstall) |
| 506 |
| 507 @decorators.WithTimeoutAndRetriesDefaults( |
| 508 INSTALL_DEFAULT_TIMEOUT, |
| 509 INSTALL_DEFAULT_RETRIES) |
| 510 def InstallSplitApk(self, base_apk, split_apks, reinstall=False, |
| 511 timeout=None, retries=None): |
| 512 """Install a split APK. |
| 513 |
| 514 Noop if all of the APK splits are already installed. |
| 515 |
| 516 Args: |
| 517 base_apk: A string of the path to the base APK. |
| 518 split_apks: A list of strings of paths of all of the APK splits. |
| 519 reinstall: A boolean indicating if we should keep any existing app data. |
| 520 timeout: timeout in seconds |
| 521 retries: number of retries |
| 522 |
| 523 Raises: |
| 524 CommandFailedError if the installation fails. |
| 525 CommandTimeoutError if the installation times out. |
| 526 DeviceUnreachableError on missing device. |
| 527 DeviceVersionError if device SDK is less than Android L. |
| 528 """ |
| 529 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
| 530 |
| 531 all_apks = [base_apk] + split_select.SelectSplits( |
| 532 self, base_apk, split_apks) |
| 533 package_name = apk_helper.GetPackageName(base_apk) |
| 534 device_apk_paths = self.GetApplicationPaths(package_name) |
| 535 |
| 536 if device_apk_paths: |
| 537 partial_install_package = package_name |
| 538 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) |
| 539 host_checksums = md5sum.CalculateHostMd5Sums(all_apks) |
| 540 apks_to_install = [k for (k, v) in host_checksums.iteritems() |
| 541 if v not in device_checksums.values()] |
| 542 if apks_to_install and not reinstall: |
| 543 self.adb.Uninstall(package_name) |
| 544 partial_install_package = None |
| 545 apks_to_install = all_apks |
| 546 else: |
| 547 partial_install_package = None |
| 548 apks_to_install = all_apks |
| 549 if apks_to_install: |
| 550 self.adb.InstallMultiple( |
| 551 apks_to_install, partial=partial_install_package, reinstall=reinstall) |
| 552 |
| 553 def _CheckSdkLevel(self, required_sdk_level): |
| 554 """Raises an exception if the device does not have the required SDK level. |
| 555 """ |
| 556 if self.build_version_sdk < required_sdk_level: |
| 557 raise device_errors.DeviceVersionError( |
| 558 ('Requires SDK level %s, device is SDK level %s' % |
| 559 (required_sdk_level, self.build_version_sdk)), |
| 560 device_serial=self.adb.GetDeviceSerial()) |
| 561 |
| 562 |
| 563 @decorators.WithTimeoutAndRetriesFromInstance() |
| 564 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
| 565 as_root=False, single_line=False, large_output=False, |
| 566 timeout=None, retries=None): |
| 567 """Run an ADB shell command. |
| 568 |
| 569 The command to run |cmd| should be a sequence of program arguments or else |
| 570 a single string. |
| 571 |
| 572 When |cmd| is a sequence, it is assumed to contain the name of the command |
| 573 to run followed by its arguments. In this case, arguments are passed to the |
| 574 command exactly as given, without any further processing by the shell. This |
| 575 allows to easily pass arguments containing spaces or special characters |
| 576 without having to worry about getting quoting right. Whenever possible, it |
| 577 is recomended to pass |cmd| as a sequence. |
| 578 |
| 579 When |cmd| is given as a string, it will be interpreted and run by the |
| 580 shell on the device. |
| 581 |
| 582 This behaviour is consistent with that of command runners in cmd_helper as |
| 583 well as Python's own subprocess.Popen. |
| 584 |
| 585 TODO(perezju) Change the default of |check_return| to True when callers |
| 586 have switched to the new behaviour. |
| 587 |
| 588 Args: |
| 589 cmd: A string with the full command to run on the device, or a sequence |
| 590 containing the command and its arguments. |
| 591 check_return: A boolean indicating whether or not the return code should |
| 592 be checked. |
| 593 cwd: The device directory in which the command should be run. |
| 594 env: The environment variables with which the command should be run. |
| 595 as_root: A boolean indicating whether the shell command should be run |
| 596 with root privileges. |
| 597 single_line: A boolean indicating if only a single line of output is |
| 598 expected. |
| 599 large_output: Uses a work-around for large shell command output. Without |
| 600 this large output will be truncated. |
| 601 timeout: timeout in seconds |
| 602 retries: number of retries |
| 603 |
| 604 Returns: |
| 605 If single_line is False, the output of the command as a list of lines, |
| 606 otherwise, a string with the unique line of output emmited by the command |
| 607 (with the optional newline at the end stripped). |
| 608 |
| 609 Raises: |
| 610 AdbCommandFailedError if check_return is True and the exit code of |
| 611 the command run on the device is non-zero. |
| 612 CommandFailedError if single_line is True but the output contains two or |
| 613 more lines. |
| 614 CommandTimeoutError on timeout. |
| 615 DeviceUnreachableError on missing device. |
| 616 """ |
| 617 def env_quote(key, value): |
| 618 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
| 619 raise KeyError('Invalid shell variable name %r' % key) |
| 620 # using double quotes here to allow interpolation of shell variables |
| 621 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
| 622 |
| 623 def run(cmd): |
| 624 return self.adb.Shell(cmd) |
| 625 |
| 626 def handle_check_return(cmd): |
| 627 try: |
| 628 return run(cmd) |
| 629 except device_errors.AdbCommandFailedError as exc: |
| 630 if check_return: |
| 631 raise |
| 632 else: |
| 633 return exc.output |
| 634 |
| 635 def handle_large_command(cmd): |
| 636 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: |
| 637 return handle_check_return(cmd) |
| 638 else: |
| 639 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
| 640 self._WriteFileWithPush(script.name, cmd) |
| 641 logging.info('Large shell command will be run from file: %s ...', |
| 642 cmd[:100]) |
| 643 return handle_check_return('sh %s' % script.name_quoted) |
| 644 |
| 645 def handle_large_output(cmd, large_output_mode): |
| 646 if large_output_mode: |
| 647 with device_temp_file.DeviceTempFile(self.adb) as large_output_file: |
| 648 cmd = '%s > %s' % (cmd, large_output_file.name) |
| 649 logging.debug('Large output mode enabled. Will write output to ' |
| 650 'device and read results from file.') |
| 651 handle_large_command(cmd) |
| 652 return self.ReadFile(large_output_file.name, force_pull=True) |
| 653 else: |
| 654 try: |
| 655 return handle_large_command(cmd) |
| 656 except device_errors.AdbCommandFailedError as exc: |
| 657 if exc.status is None: |
| 658 logging.exception('No output found for %s', cmd) |
| 659 logging.warning('Attempting to run in large_output mode.') |
| 660 logging.warning('Use RunShellCommand(..., large_output=True) for ' |
| 661 'shell commands that expect a lot of output.') |
| 662 return handle_large_output(cmd, True) |
| 663 else: |
| 664 raise |
| 665 |
| 666 if not isinstance(cmd, basestring): |
| 667 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
| 668 if env: |
| 669 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
| 670 cmd = '%s %s' % (env, cmd) |
| 671 if cwd: |
| 672 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
| 673 if as_root and self.NeedsSU(): |
| 674 # "su -c sh -c" allows using shell features in |cmd| |
| 675 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
| 676 |
| 677 output = handle_large_output(cmd, large_output).splitlines() |
| 678 |
| 679 if single_line: |
| 680 if not output: |
| 681 return '' |
| 682 elif len(output) == 1: |
| 683 return output[0] |
| 684 else: |
| 685 msg = 'one line of output was expected, but got: %s' |
| 686 raise device_errors.CommandFailedError(msg % output, str(self)) |
| 687 else: |
| 688 return output |
| 689 |
| 690 def _RunPipedShellCommand(self, script, **kwargs): |
| 691 PIPESTATUS_LEADER = 'PIPESTATUS: ' |
| 692 |
| 693 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER |
| 694 kwargs['check_return'] = True |
| 695 output = self.RunShellCommand(script, **kwargs) |
| 696 pipestatus_line = output[-1] |
| 697 |
| 698 if not pipestatus_line.startswith(PIPESTATUS_LEADER): |
| 699 logging.error('Pipe exit statuses of shell script missing.') |
| 700 raise device_errors.AdbShellCommandFailedError( |
| 701 script, output, status=None, |
| 702 device_serial=self.adb.GetDeviceSerial()) |
| 703 |
| 704 output = output[:-1] |
| 705 statuses = [ |
| 706 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()] |
| 707 if any(statuses): |
| 708 raise device_errors.AdbShellCommandFailedError( |
| 709 script, output, status=statuses, |
| 710 device_serial=self.adb.GetDeviceSerial()) |
| 711 return output |
| 712 |
| 713 @decorators.WithTimeoutAndRetriesFromInstance() |
| 714 def KillAll(self, process_name, signum=device_signal.SIGKILL, as_root=False, |
| 715 blocking=False, quiet=False, timeout=None, retries=None): |
| 716 """Kill all processes with the given name on the device. |
| 717 |
| 718 Args: |
| 719 process_name: A string containing the name of the process to kill. |
| 720 signum: An integer containing the signal number to send to kill. Defaults |
| 721 to SIGKILL (9). |
| 722 as_root: A boolean indicating whether the kill should be executed with |
| 723 root privileges. |
| 724 blocking: A boolean indicating whether we should wait until all processes |
| 725 with the given |process_name| are dead. |
| 726 quiet: A boolean indicating whether to ignore the fact that no processes |
| 727 to kill were found. |
| 728 timeout: timeout in seconds |
| 729 retries: number of retries |
| 730 |
| 731 Returns: |
| 732 The number of processes attempted to kill. |
| 733 |
| 734 Raises: |
| 735 CommandFailedError if no process was killed and |quiet| is False. |
| 736 CommandTimeoutError on timeout. |
| 737 DeviceUnreachableError on missing device. |
| 738 """ |
| 739 pids = self.GetPids(process_name) |
| 740 if not pids: |
| 741 if quiet: |
| 742 return 0 |
| 743 else: |
| 744 raise device_errors.CommandFailedError( |
| 745 'No process "%s"' % process_name, str(self)) |
| 746 |
| 747 cmd = ['kill', '-%d' % signum] + pids.values() |
| 748 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| 749 |
| 750 if blocking: |
| 751 # TODO(perezu): use timeout_retry.WaitFor |
| 752 wait_period = 0.1 |
| 753 while self.GetPids(process_name): |
| 754 time.sleep(wait_period) |
| 755 |
| 756 return len(pids) |
| 757 |
| 758 @decorators.WithTimeoutAndRetriesFromInstance() |
| 759 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, |
| 760 force_stop=False, timeout=None, retries=None): |
| 761 """Start package's activity on the device. |
| 762 |
| 763 Args: |
| 764 intent_obj: An Intent object to send. |
| 765 blocking: A boolean indicating whether we should wait for the activity to |
| 766 finish launching. |
| 767 trace_file_name: If present, a string that both indicates that we want to |
| 768 profile the activity and contains the path to which the |
| 769 trace should be saved. |
| 770 force_stop: A boolean indicating whether we should stop the activity |
| 771 before starting it. |
| 772 timeout: timeout in seconds |
| 773 retries: number of retries |
| 774 |
| 775 Raises: |
| 776 CommandFailedError if the activity could not be started. |
| 777 CommandTimeoutError on timeout. |
| 778 DeviceUnreachableError on missing device. |
| 779 """ |
| 780 cmd = ['am', 'start'] |
| 781 if blocking: |
| 782 cmd.append('-W') |
| 783 if trace_file_name: |
| 784 cmd.extend(['--start-profiler', trace_file_name]) |
| 785 if force_stop: |
| 786 cmd.append('-S') |
| 787 cmd.extend(intent_obj.am_args) |
| 788 for line in self.RunShellCommand(cmd, check_return=True): |
| 789 if line.startswith('Error:'): |
| 790 raise device_errors.CommandFailedError(line, str(self)) |
| 791 |
| 792 @decorators.WithTimeoutAndRetriesFromInstance() |
| 793 def StartInstrumentation(self, component, finish=True, raw=False, |
| 794 extras=None, timeout=None, retries=None): |
| 795 if extras is None: |
| 796 extras = {} |
| 797 |
| 798 cmd = ['am', 'instrument'] |
| 799 if finish: |
| 800 cmd.append('-w') |
| 801 if raw: |
| 802 cmd.append('-r') |
| 803 for k, v in extras.iteritems(): |
| 804 cmd.extend(['-e', str(k), str(v)]) |
| 805 cmd.append(component) |
| 806 return self.RunShellCommand(cmd, check_return=True, large_output=True) |
| 807 |
| 808 @decorators.WithTimeoutAndRetriesFromInstance() |
| 809 def BroadcastIntent(self, intent_obj, timeout=None, retries=None): |
| 810 """Send a broadcast intent. |
| 811 |
| 812 Args: |
| 813 intent: An Intent to broadcast. |
| 814 timeout: timeout in seconds |
| 815 retries: number of retries |
| 816 |
| 817 Raises: |
| 818 CommandTimeoutError on timeout. |
| 819 DeviceUnreachableError on missing device. |
| 820 """ |
| 821 cmd = ['am', 'broadcast'] + intent_obj.am_args |
| 822 self.RunShellCommand(cmd, check_return=True) |
| 823 |
| 824 @decorators.WithTimeoutAndRetriesFromInstance() |
| 825 def GoHome(self, timeout=None, retries=None): |
| 826 """Return to the home screen and obtain launcher focus. |
| 827 |
| 828 This command launches the home screen and attempts to obtain |
| 829 launcher focus until the timeout is reached. |
| 830 |
| 831 Args: |
| 832 timeout: timeout in seconds |
| 833 retries: number of retries |
| 834 |
| 835 Raises: |
| 836 CommandTimeoutError on timeout. |
| 837 DeviceUnreachableError on missing device. |
| 838 """ |
| 839 def is_launcher_focused(): |
| 840 output = self.RunShellCommand(['dumpsys', 'window', 'windows'], |
| 841 check_return=True, large_output=True) |
| 842 return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output) |
| 843 |
| 844 def dismiss_popups(): |
| 845 # There is a dialog present; attempt to get rid of it. |
| 846 # Not all dialogs can be dismissed with back. |
| 847 self.SendKeyEvent(keyevent.KEYCODE_ENTER) |
| 848 self.SendKeyEvent(keyevent.KEYCODE_BACK) |
| 849 return is_launcher_focused() |
| 850 |
| 851 # If Home is already focused, return early to avoid unnecessary work. |
| 852 if is_launcher_focused(): |
| 853 return |
| 854 |
| 855 self.StartActivity( |
| 856 intent.Intent(action='android.intent.action.MAIN', |
| 857 category='android.intent.category.HOME'), |
| 858 blocking=True) |
| 859 |
| 860 if not is_launcher_focused(): |
| 861 timeout_retry.WaitFor(dismiss_popups, wait_period=1) |
| 862 |
| 863 @decorators.WithTimeoutAndRetriesFromInstance() |
| 864 def ForceStop(self, package, timeout=None, retries=None): |
| 865 """Close the application. |
| 866 |
| 867 Args: |
| 868 package: A string containing the name of the package to stop. |
| 869 timeout: timeout in seconds |
| 870 retries: number of retries |
| 871 |
| 872 Raises: |
| 873 CommandTimeoutError on timeout. |
| 874 DeviceUnreachableError on missing device. |
| 875 """ |
| 876 self.RunShellCommand(['am', 'force-stop', package], check_return=True) |
| 877 |
| 878 @decorators.WithTimeoutAndRetriesFromInstance() |
| 879 def ClearApplicationState(self, package, timeout=None, retries=None): |
| 880 """Clear all state for the given package. |
| 881 |
| 882 Args: |
| 883 package: A string containing the name of the package to stop. |
| 884 timeout: timeout in seconds |
| 885 retries: number of retries |
| 886 |
| 887 Raises: |
| 888 CommandTimeoutError on timeout. |
| 889 DeviceUnreachableError on missing device. |
| 890 """ |
| 891 # Check that the package exists before clearing it for android builds below |
| 892 # JB MR2. Necessary because calling pm clear on a package that doesn't exist |
| 893 # may never return. |
| 894 if ((self.build_version_sdk >= |
| 895 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) |
| 896 or self.GetApplicationPaths(package)): |
| 897 self.RunShellCommand(['pm', 'clear', package], check_return=True) |
| 898 |
| 899 @decorators.WithTimeoutAndRetriesFromInstance() |
| 900 def SendKeyEvent(self, keycode, timeout=None, retries=None): |
| 901 """Sends a keycode to the device. |
| 902 |
| 903 See the pylib.constants.keyevent module for suitable keycode values. |
| 904 |
| 905 Args: |
| 906 keycode: A integer keycode to send to the device. |
| 907 timeout: timeout in seconds |
| 908 retries: number of retries |
| 909 |
| 910 Raises: |
| 911 CommandTimeoutError on timeout. |
| 912 DeviceUnreachableError on missing device. |
| 913 """ |
| 914 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], |
| 915 check_return=True) |
| 916 |
| 917 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
| 918 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 919 |
| 920 @decorators.WithTimeoutAndRetriesDefaults( |
| 921 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, |
| 922 PUSH_CHANGED_FILES_DEFAULT_RETRIES) |
| 923 def PushChangedFiles(self, host_device_tuples, timeout=None, |
| 924 retries=None, delete_device_stale=False): |
| 925 """Push files to the device, skipping files that don't need updating. |
| 926 |
| 927 When a directory is pushed, it is traversed recursively on the host and |
| 928 all files in it are pushed to the device as needed. |
| 929 Additionally, if delete_device_stale option is True, |
| 930 files that exist on the device but don't exist on the host are deleted. |
| 931 |
| 932 Args: |
| 933 host_device_tuples: A list of (host_path, device_path) tuples, where |
| 934 |host_path| is an absolute path of a file or directory on the host |
| 935 that should be minimially pushed to the device, and |device_path| is |
| 936 an absolute path of the destination on the device. |
| 937 timeout: timeout in seconds |
| 938 retries: number of retries |
| 939 delete_device_stale: option to delete stale files on device |
| 940 |
| 941 Raises: |
| 942 CommandFailedError on failure. |
| 943 CommandTimeoutError on timeout. |
| 944 DeviceUnreachableError on missing device. |
| 945 """ |
| 946 |
| 947 all_changed_files = [] |
| 948 all_stale_files = [] |
| 949 for h, d in host_device_tuples: |
| 950 if os.path.isdir(h): |
| 951 self.RunShellCommand(['mkdir', '-p', d], check_return=True) |
| 952 changed_files, stale_files = ( |
| 953 self._GetChangedAndStaleFiles(h, d, delete_device_stale)) |
| 954 all_changed_files += changed_files |
| 955 all_stale_files += stale_files |
| 956 |
| 957 if delete_device_stale: |
| 958 self.RunShellCommand(['rm', '-f'] + all_stale_files, |
| 959 check_return=True) |
| 960 |
| 961 if not all_changed_files: |
| 962 return |
| 963 |
| 964 self._PushFilesImpl(host_device_tuples, all_changed_files) |
| 965 |
| 966 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False): |
| 967 """Get files to push and delete |
| 968 |
| 969 Args: |
| 970 host_path: an absolute path of a file or directory on the host |
| 971 device_path: an absolute path of a file or directory on the device |
| 972 track_stale: whether to bother looking for stale files (slower) |
| 973 |
| 974 Returns: |
| 975 a two-element tuple |
| 976 1st element: a list of (host_files_path, device_files_path) tuples to push |
| 977 2nd element: a list of stale files under device_path, or [] when |
| 978 track_stale == False |
| 979 """ |
| 980 real_host_path = os.path.realpath(host_path) |
| 981 try: |
| 982 real_device_path = self.RunShellCommand( |
| 983 ['realpath', device_path], single_line=True, check_return=True) |
| 984 except device_errors.CommandFailedError: |
| 985 real_device_path = None |
| 986 if not real_device_path: |
| 987 return ([(host_path, device_path)], []) |
| 988 |
| 989 try: |
| 990 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) |
| 991 interesting_device_paths = [real_device_path] |
| 992 if not track_stale and os.path.isdir(real_host_path): |
| 993 interesting_device_paths = [ |
| 994 posixpath.join(real_device_path, os.path.relpath(p, real_host_path)) |
| 995 for p in host_checksums.keys()] |
| 996 device_checksums = md5sum.CalculateDeviceMd5Sums( |
| 997 interesting_device_paths, self) |
| 998 except EnvironmentError as e: |
| 999 logging.warning('Error calculating md5: %s', e) |
| 1000 return ([(host_path, device_path)], []) |
| 1001 |
| 1002 if os.path.isfile(host_path): |
| 1003 host_checksum = host_checksums.get(real_host_path) |
| 1004 device_checksum = device_checksums.get(real_device_path) |
| 1005 if host_checksum != device_checksum: |
| 1006 return ([(host_path, device_path)], []) |
| 1007 else: |
| 1008 return ([], []) |
| 1009 else: |
| 1010 to_push = [] |
| 1011 for host_abs_path, host_checksum in host_checksums.iteritems(): |
| 1012 device_abs_path = '%s/%s' % ( |
| 1013 real_device_path, os.path.relpath(host_abs_path, real_host_path)) |
| 1014 device_checksum = device_checksums.pop(device_abs_path, None) |
| 1015 if device_checksum != host_checksum: |
| 1016 to_push.append((host_abs_path, device_abs_path)) |
| 1017 to_delete = device_checksums.keys() |
| 1018 return (to_push, to_delete) |
| 1019 |
| 1020 def _PushFilesImpl(self, host_device_tuples, files): |
| 1021 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) |
| 1022 file_count = len(files) |
| 1023 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) |
| 1024 for h, _ in host_device_tuples) |
| 1025 dir_file_count = 0 |
| 1026 for h, _ in host_device_tuples: |
| 1027 if os.path.isdir(h): |
| 1028 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) |
| 1029 else: |
| 1030 dir_file_count += 1 |
| 1031 |
| 1032 push_duration = self._ApproximateDuration( |
| 1033 file_count, file_count, size, False) |
| 1034 dir_push_duration = self._ApproximateDuration( |
| 1035 len(host_device_tuples), dir_file_count, dir_size, False) |
| 1036 zip_duration = self._ApproximateDuration(1, 1, size, True) |
| 1037 |
| 1038 self._InstallCommands() |
| 1039 |
| 1040 if dir_push_duration < push_duration and ( |
| 1041 dir_push_duration < zip_duration or not self._commands_installed): |
| 1042 self._PushChangedFilesIndividually(host_device_tuples) |
| 1043 elif push_duration < zip_duration or not self._commands_installed: |
| 1044 self._PushChangedFilesIndividually(files) |
| 1045 else: |
| 1046 self._PushChangedFilesZipped(files) |
| 1047 self.RunShellCommand( |
| 1048 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], |
| 1049 as_root=True, check_return=True) |
| 1050 |
| 1051 def _InstallCommands(self): |
| 1052 if self._commands_installed is None: |
| 1053 try: |
| 1054 if not install_commands.Installed(self): |
| 1055 install_commands.InstallCommands(self) |
| 1056 self._commands_installed = True |
| 1057 except Exception as e: |
| 1058 logging.warning('unzip not available: %s' % str(e)) |
| 1059 self._commands_installed = False |
| 1060 |
| 1061 @staticmethod |
| 1062 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): |
| 1063 # We approximate the time to push a set of files to a device as: |
| 1064 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where |
| 1065 # t: total time (sec) |
| 1066 # c1: adb call time delay (sec) |
| 1067 # a: number of times adb is called (unitless) |
| 1068 # c2: push time delay (sec) |
| 1069 # f: number of files pushed via adb (unitless) |
| 1070 # c3: zip time delay (sec) |
| 1071 # c4: zip rate (bytes/sec) |
| 1072 # b: total number of bytes (bytes) |
| 1073 # c5: transfer rate (bytes/sec) |
| 1074 # c6: compression ratio (unitless) |
| 1075 |
| 1076 # All of these are approximations. |
| 1077 ADB_CALL_PENALTY = 0.1 # seconds |
| 1078 ADB_PUSH_PENALTY = 0.01 # seconds |
| 1079 ZIP_PENALTY = 2.0 # seconds |
| 1080 ZIP_RATE = 10000000.0 # bytes / second |
| 1081 TRANSFER_RATE = 2000000.0 # bytes / second |
| 1082 COMPRESSION_RATIO = 2.0 # unitless |
| 1083 |
| 1084 adb_call_time = ADB_CALL_PENALTY * adb_calls |
| 1085 adb_push_setup_time = ADB_PUSH_PENALTY * file_count |
| 1086 if is_zipping: |
| 1087 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE |
| 1088 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO) |
| 1089 else: |
| 1090 zip_time = 0 |
| 1091 transfer_time = byte_count / TRANSFER_RATE |
| 1092 return adb_call_time + adb_push_setup_time + zip_time + transfer_time |
| 1093 |
| 1094 def _PushChangedFilesIndividually(self, files): |
| 1095 for h, d in files: |
| 1096 self.adb.Push(h, d) |
| 1097 |
| 1098 def _PushChangedFilesZipped(self, files): |
| 1099 if not files: |
| 1100 return |
| 1101 |
| 1102 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: |
| 1103 zip_proc = multiprocessing.Process( |
| 1104 target=DeviceUtils._CreateDeviceZip, |
| 1105 args=(zip_file.name, files)) |
| 1106 zip_proc.start() |
| 1107 zip_proc.join() |
| 1108 |
| 1109 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath() |
| 1110 try: |
| 1111 self.adb.Push(zip_file.name, zip_on_device) |
| 1112 self.RunShellCommand( |
| 1113 ['unzip', zip_on_device], |
| 1114 as_root=True, |
| 1115 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR}, |
| 1116 check_return=True) |
| 1117 finally: |
| 1118 if zip_proc.is_alive(): |
| 1119 zip_proc.terminate() |
| 1120 if self.IsOnline(): |
| 1121 self.RunShellCommand(['rm', zip_on_device], check_return=True) |
| 1122 |
| 1123 @staticmethod |
| 1124 def _CreateDeviceZip(zip_path, host_device_tuples): |
| 1125 with zipfile.ZipFile(zip_path, 'w') as zip_file: |
| 1126 for host_path, device_path in host_device_tuples: |
| 1127 zip_utils.WriteToZipFile(zip_file, host_path, device_path) |
| 1128 |
| 1129 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1130 def FileExists(self, device_path, timeout=None, retries=None): |
| 1131 """Checks whether the given file exists on the device. |
| 1132 |
| 1133 Args: |
| 1134 device_path: A string containing the absolute path to the file on the |
| 1135 device. |
| 1136 timeout: timeout in seconds |
| 1137 retries: number of retries |
| 1138 |
| 1139 Returns: |
| 1140 True if the file exists on the device, False otherwise. |
| 1141 |
| 1142 Raises: |
| 1143 CommandTimeoutError on timeout. |
| 1144 DeviceUnreachableError on missing device. |
| 1145 """ |
| 1146 try: |
| 1147 self.RunShellCommand(['test', '-e', device_path], check_return=True) |
| 1148 return True |
| 1149 except device_errors.AdbCommandFailedError: |
| 1150 return False |
| 1151 |
| 1152 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1153 def PullFile(self, device_path, host_path, timeout=None, retries=None): |
| 1154 """Pull a file from the device. |
| 1155 |
| 1156 Args: |
| 1157 device_path: A string containing the absolute path of the file to pull |
| 1158 from the device. |
| 1159 host_path: A string containing the absolute path of the destination on |
| 1160 the host. |
| 1161 timeout: timeout in seconds |
| 1162 retries: number of retries |
| 1163 |
| 1164 Raises: |
| 1165 CommandFailedError on failure. |
| 1166 CommandTimeoutError on timeout. |
| 1167 """ |
| 1168 # Create the base dir if it doesn't exist already |
| 1169 dirname = os.path.dirname(host_path) |
| 1170 if dirname and not os.path.exists(dirname): |
| 1171 os.makedirs(dirname) |
| 1172 self.adb.Pull(device_path, host_path) |
| 1173 |
| 1174 def _ReadFileWithPull(self, device_path): |
| 1175 try: |
| 1176 d = tempfile.mkdtemp() |
| 1177 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull') |
| 1178 self.adb.Pull(device_path, host_temp_path) |
| 1179 with open(host_temp_path, 'r') as host_temp: |
| 1180 return host_temp.read() |
| 1181 finally: |
| 1182 if os.path.exists(d): |
| 1183 shutil.rmtree(d) |
| 1184 |
| 1185 _LS_RE = re.compile( |
| 1186 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?' |
| 1187 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$') |
| 1188 |
| 1189 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1190 def ReadFile(self, device_path, as_root=False, force_pull=False, |
| 1191 timeout=None, retries=None): |
| 1192 """Reads the contents of a file from the device. |
| 1193 |
| 1194 Args: |
| 1195 device_path: A string containing the absolute path of the file to read |
| 1196 from the device. |
| 1197 as_root: A boolean indicating whether the read should be executed with |
| 1198 root privileges. |
| 1199 force_pull: A boolean indicating whether to force the operation to be |
| 1200 performed by pulling a file from the device. The default is, when the |
| 1201 contents are short, to retrieve the contents using cat instead. |
| 1202 timeout: timeout in seconds |
| 1203 retries: number of retries |
| 1204 |
| 1205 Returns: |
| 1206 The contents of |device_path| as a string. Contents are intepreted using |
| 1207 universal newlines, so the caller will see them encoded as '\n'. Also, |
| 1208 all lines will be terminated. |
| 1209 |
| 1210 Raises: |
| 1211 AdbCommandFailedError if the file can't be read. |
| 1212 CommandTimeoutError on timeout. |
| 1213 DeviceUnreachableError on missing device. |
| 1214 """ |
| 1215 def get_size(path): |
| 1216 # TODO(jbudorick): Implement a generic version of Stat() that handles |
| 1217 # as_root=True, then switch this implementation to use that. |
| 1218 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root, |
| 1219 check_return=True) |
| 1220 for line in ls_out: |
| 1221 m = self._LS_RE.match(line) |
| 1222 if m and m.group('name') == posixpath.basename(device_path): |
| 1223 return int(m.group('size')) |
| 1224 logging.warning('Could not determine size of %s.', device_path) |
| 1225 return None |
| 1226 |
| 1227 if (not force_pull |
| 1228 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH): |
| 1229 return _JoinLines(self.RunShellCommand( |
| 1230 ['cat', device_path], as_root=as_root, check_return=True)) |
| 1231 elif as_root and self.NeedsSU(): |
| 1232 with device_temp_file.DeviceTempFile(self.adb) as device_temp: |
| 1233 self.RunShellCommand(['cp', device_path, device_temp.name], |
| 1234 as_root=True, check_return=True) |
| 1235 return self._ReadFileWithPull(device_temp.name) |
| 1236 else: |
| 1237 return self._ReadFileWithPull(device_path) |
| 1238 |
| 1239 def _WriteFileWithPush(self, device_path, contents): |
| 1240 with tempfile.NamedTemporaryFile() as host_temp: |
| 1241 host_temp.write(contents) |
| 1242 host_temp.flush() |
| 1243 self.adb.Push(host_temp.name, device_path) |
| 1244 |
| 1245 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1246 def WriteFile(self, device_path, contents, as_root=False, force_push=False, |
| 1247 timeout=None, retries=None): |
| 1248 """Writes |contents| to a file on the device. |
| 1249 |
| 1250 Args: |
| 1251 device_path: A string containing the absolute path to the file to write |
| 1252 on the device. |
| 1253 contents: A string containing the data to write to the device. |
| 1254 as_root: A boolean indicating whether the write should be executed with |
| 1255 root privileges (if available). |
| 1256 force_push: A boolean indicating whether to force the operation to be |
| 1257 performed by pushing a file to the device. The default is, when the |
| 1258 contents are short, to pass the contents using a shell script instead. |
| 1259 timeout: timeout in seconds |
| 1260 retries: number of retries |
| 1261 |
| 1262 Raises: |
| 1263 CommandFailedError if the file could not be written on the device. |
| 1264 CommandTimeoutError on timeout. |
| 1265 DeviceUnreachableError on missing device. |
| 1266 """ |
| 1267 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: |
| 1268 # If the contents are small, for efficieny we write the contents with |
| 1269 # a shell command rather than pushing a file. |
| 1270 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), |
| 1271 cmd_helper.SingleQuote(device_path)) |
| 1272 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| 1273 elif as_root and self.NeedsSU(): |
| 1274 # Adb does not allow to "push with su", so we first push to a temp file |
| 1275 # on a safe location, and then copy it to the desired location with su. |
| 1276 with device_temp_file.DeviceTempFile(self.adb) as device_temp: |
| 1277 self._WriteFileWithPush(device_temp.name, contents) |
| 1278 # Here we need 'cp' rather than 'mv' because the temp and |
| 1279 # destination files might be on different file systems (e.g. |
| 1280 # on internal storage and an external sd card). |
| 1281 self.RunShellCommand(['cp', device_temp.name, device_path], |
| 1282 as_root=True, check_return=True) |
| 1283 else: |
| 1284 # If root is not needed, we can push directly to the desired location. |
| 1285 self._WriteFileWithPush(device_path, contents) |
| 1286 |
| 1287 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1288 def Ls(self, device_path, timeout=None, retries=None): |
| 1289 """Lists the contents of a directory on the device. |
| 1290 |
| 1291 Args: |
| 1292 device_path: A string containing the path of the directory on the device |
| 1293 to list. |
| 1294 timeout: timeout in seconds |
| 1295 retries: number of retries |
| 1296 |
| 1297 Returns: |
| 1298 A list of pairs (filename, stat) for each file found in the directory, |
| 1299 where the stat object has the properties: st_mode, st_size, and st_time. |
| 1300 |
| 1301 Raises: |
| 1302 AdbCommandFailedError if |device_path| does not specify a valid and |
| 1303 accessible directory in the device. |
| 1304 CommandTimeoutError on timeout. |
| 1305 DeviceUnreachableError on missing device. |
| 1306 """ |
| 1307 return self.adb.Ls(device_path) |
| 1308 |
| 1309 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1310 def Stat(self, device_path, timeout=None, retries=None): |
| 1311 """Get the stat attributes of a file or directory on the device. |
| 1312 |
| 1313 Args: |
| 1314 device_path: A string containing the path of from which to get attributes |
| 1315 on the device. |
| 1316 timeout: timeout in seconds |
| 1317 retries: number of retries |
| 1318 |
| 1319 Returns: |
| 1320 A stat object with the properties: st_mode, st_size, and st_time |
| 1321 |
| 1322 Raises: |
| 1323 CommandFailedError if device_path cannot be found on the device. |
| 1324 CommandTimeoutError on timeout. |
| 1325 DeviceUnreachableError on missing device. |
| 1326 """ |
| 1327 dirname, target = device_path.rsplit('/', 1) |
| 1328 for filename, stat in self.adb.Ls(dirname): |
| 1329 if filename == target: |
| 1330 return stat |
| 1331 raise device_errors.CommandFailedError( |
| 1332 'Cannot find file or directory: %r' % device_path, str(self)) |
| 1333 |
| 1334 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1335 def SetJavaAsserts(self, enabled, timeout=None, retries=None): |
| 1336 """Enables or disables Java asserts. |
| 1337 |
| 1338 Args: |
| 1339 enabled: A boolean indicating whether Java asserts should be enabled |
| 1340 or disabled. |
| 1341 timeout: timeout in seconds |
| 1342 retries: number of retries |
| 1343 |
| 1344 Returns: |
| 1345 True if the device-side property changed and a restart is required as a |
| 1346 result, False otherwise. |
| 1347 |
| 1348 Raises: |
| 1349 CommandTimeoutError on timeout. |
| 1350 """ |
| 1351 def find_property(lines, property_name): |
| 1352 for index, line in enumerate(lines): |
| 1353 if line.strip() == '': |
| 1354 continue |
| 1355 key, value = (s.strip() for s in line.split('=', 1)) |
| 1356 if key == property_name: |
| 1357 return index, value |
| 1358 return None, '' |
| 1359 |
| 1360 new_value = 'all' if enabled else '' |
| 1361 |
| 1362 # First ensure the desired property is persisted. |
| 1363 try: |
| 1364 properties = self.ReadFile( |
| 1365 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines() |
| 1366 except device_errors.CommandFailedError: |
| 1367 properties = [] |
| 1368 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY) |
| 1369 if new_value != value: |
| 1370 if new_value: |
| 1371 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value) |
| 1372 if index is None: |
| 1373 properties.append(new_line) |
| 1374 else: |
| 1375 properties[index] = new_line |
| 1376 else: |
| 1377 assert index is not None # since new_value == '' and new_value != value |
| 1378 properties.pop(index) |
| 1379 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH, |
| 1380 _JoinLines(properties)) |
| 1381 |
| 1382 # Next, check the current runtime value is what we need, and |
| 1383 # if not, set it and report that a reboot is required. |
| 1384 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) |
| 1385 if new_value != value: |
| 1386 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) |
| 1387 return True |
| 1388 else: |
| 1389 return False |
| 1390 |
| 1391 @property |
| 1392 def language(self): |
| 1393 """Returns the language setting on the device.""" |
| 1394 return self.GetProp('persist.sys.language', cache=False) |
| 1395 |
| 1396 @property |
| 1397 def country(self): |
| 1398 """Returns the country setting on the device.""" |
| 1399 return self.GetProp('persist.sys.country', cache=False) |
| 1400 |
| 1401 @property |
| 1402 def screen_density(self): |
| 1403 """Returns the screen density of the device.""" |
| 1404 DPI_TO_DENSITY = { |
| 1405 120: 'ldpi', |
| 1406 160: 'mdpi', |
| 1407 240: 'hdpi', |
| 1408 320: 'xhdpi', |
| 1409 480: 'xxhdpi', |
| 1410 640: 'xxxhdpi', |
| 1411 } |
| 1412 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True)) |
| 1413 return DPI_TO_DENSITY.get(dpi, 'tvdpi') |
| 1414 |
| 1415 @property |
| 1416 def build_description(self): |
| 1417 """Returns the build description of the system. |
| 1418 |
| 1419 For example: |
| 1420 nakasi-user 4.4.4 KTU84P 1227136 release-keys |
| 1421 """ |
| 1422 return self.GetProp('ro.build.description', cache=True) |
| 1423 |
| 1424 @property |
| 1425 def build_fingerprint(self): |
| 1426 """Returns the build fingerprint of the system. |
| 1427 |
| 1428 For example: |
| 1429 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys |
| 1430 """ |
| 1431 return self.GetProp('ro.build.fingerprint', cache=True) |
| 1432 |
| 1433 @property |
| 1434 def build_id(self): |
| 1435 """Returns the build ID of the system (e.g. 'KTU84P').""" |
| 1436 return self.GetProp('ro.build.id', cache=True) |
| 1437 |
| 1438 @property |
| 1439 def build_product(self): |
| 1440 """Returns the build product of the system (e.g. 'grouper').""" |
| 1441 return self.GetProp('ro.build.product', cache=True) |
| 1442 |
| 1443 @property |
| 1444 def build_type(self): |
| 1445 """Returns the build type of the system (e.g. 'user').""" |
| 1446 return self.GetProp('ro.build.type', cache=True) |
| 1447 |
| 1448 @property |
| 1449 def build_version_sdk(self): |
| 1450 """Returns the build version sdk of the system as a number (e.g. 19). |
| 1451 |
| 1452 For version code numbers see: |
| 1453 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html |
| 1454 |
| 1455 For named constants see: |
| 1456 pylib.constants.ANDROID_SDK_VERSION_CODES |
| 1457 |
| 1458 Raises: |
| 1459 CommandFailedError if the build version sdk is not a number. |
| 1460 """ |
| 1461 value = self.GetProp('ro.build.version.sdk', cache=True) |
| 1462 try: |
| 1463 return int(value) |
| 1464 except ValueError: |
| 1465 raise device_errors.CommandFailedError( |
| 1466 'Invalid build version sdk: %r' % value) |
| 1467 |
| 1468 @property |
| 1469 def product_cpu_abi(self): |
| 1470 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a').""" |
| 1471 return self.GetProp('ro.product.cpu.abi', cache=True) |
| 1472 |
| 1473 @property |
| 1474 def product_model(self): |
| 1475 """Returns the name of the product model (e.g. 'Nexus 7').""" |
| 1476 return self.GetProp('ro.product.model', cache=True) |
| 1477 |
| 1478 @property |
| 1479 def product_name(self): |
| 1480 """Returns the product name of the device (e.g. 'nakasi').""" |
| 1481 return self.GetProp('ro.product.name', cache=True) |
| 1482 |
| 1483 def GetProp(self, property_name, cache=False, timeout=DEFAULT, |
| 1484 retries=DEFAULT): |
| 1485 """Gets a property from the device. |
| 1486 |
| 1487 Args: |
| 1488 property_name: A string containing the name of the property to get from |
| 1489 the device. |
| 1490 cache: A boolean indicating whether to cache the value of this property. |
| 1491 timeout: timeout in seconds |
| 1492 retries: number of retries |
| 1493 |
| 1494 Returns: |
| 1495 The value of the device's |property_name| property. |
| 1496 |
| 1497 Raises: |
| 1498 CommandTimeoutError on timeout. |
| 1499 """ |
| 1500 assert isinstance(property_name, basestring), ( |
| 1501 "property_name is not a string: %r" % property_name) |
| 1502 |
| 1503 cache_key = '_prop:' + property_name |
| 1504 if cache and cache_key in self._cache: |
| 1505 return self._cache[cache_key] |
| 1506 else: |
| 1507 # timeout and retries are handled down at run shell, because we don't |
| 1508 # want to apply them in the other branch when reading from the cache |
| 1509 value = self.RunShellCommand( |
| 1510 ['getprop', property_name], single_line=True, check_return=True, |
| 1511 timeout=self._default_timeout if timeout is DEFAULT else timeout, |
| 1512 retries=self._default_retries if retries is DEFAULT else retries) |
| 1513 if cache or cache_key in self._cache: |
| 1514 self._cache[cache_key] = value |
| 1515 return value |
| 1516 |
| 1517 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1518 def SetProp(self, property_name, value, check=False, timeout=None, |
| 1519 retries=None): |
| 1520 """Sets a property on the device. |
| 1521 |
| 1522 Args: |
| 1523 property_name: A string containing the name of the property to set on |
| 1524 the device. |
| 1525 value: A string containing the value to set to the property on the |
| 1526 device. |
| 1527 check: A boolean indicating whether to check that the property was |
| 1528 successfully set on the device. |
| 1529 timeout: timeout in seconds |
| 1530 retries: number of retries |
| 1531 |
| 1532 Raises: |
| 1533 CommandFailedError if check is true and the property was not correctly |
| 1534 set on the device (e.g. because it is not rooted). |
| 1535 CommandTimeoutError on timeout. |
| 1536 """ |
| 1537 assert isinstance(property_name, basestring), ( |
| 1538 "property_name is not a string: %r" % property_name) |
| 1539 assert isinstance(value, basestring), "value is not a string: %r" % value |
| 1540 |
| 1541 self.RunShellCommand(['setprop', property_name, value], check_return=True) |
| 1542 if property_name in self._cache: |
| 1543 del self._cache[property_name] |
| 1544 # TODO(perezju) remove the option and make the check mandatory, but using a |
| 1545 # single shell script to both set- and getprop. |
| 1546 if check and value != self.GetProp(property_name): |
| 1547 raise device_errors.CommandFailedError( |
| 1548 'Unable to set property %r on the device to %r' |
| 1549 % (property_name, value), str(self)) |
| 1550 |
| 1551 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1552 def GetABI(self, timeout=None, retries=None): |
| 1553 """Gets the device main ABI. |
| 1554 |
| 1555 Args: |
| 1556 timeout: timeout in seconds |
| 1557 retries: number of retries |
| 1558 |
| 1559 Returns: |
| 1560 The device's main ABI name. |
| 1561 |
| 1562 Raises: |
| 1563 CommandTimeoutError on timeout. |
| 1564 """ |
| 1565 return self.GetProp('ro.product.cpu.abi') |
| 1566 |
| 1567 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1568 def GetPids(self, process_name, timeout=None, retries=None): |
| 1569 """Returns the PIDs of processes with the given name. |
| 1570 |
| 1571 Note that the |process_name| is often the package name. |
| 1572 |
| 1573 Args: |
| 1574 process_name: A string containing the process name to get the PIDs for. |
| 1575 timeout: timeout in seconds |
| 1576 retries: number of retries |
| 1577 |
| 1578 Returns: |
| 1579 A dict mapping process name to PID for each process that contained the |
| 1580 provided |process_name|. |
| 1581 |
| 1582 Raises: |
| 1583 CommandTimeoutError on timeout. |
| 1584 DeviceUnreachableError on missing device. |
| 1585 """ |
| 1586 procs_pids = {} |
| 1587 try: |
| 1588 ps_output = self._RunPipedShellCommand( |
| 1589 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name)) |
| 1590 except device_errors.AdbShellCommandFailedError as e: |
| 1591 if e.status and isinstance(e.status, list) and not e.status[0]: |
| 1592 # If ps succeeded but grep failed, there were no processes with the |
| 1593 # given name. |
| 1594 return procs_pids |
| 1595 else: |
| 1596 raise |
| 1597 |
| 1598 for line in ps_output: |
| 1599 try: |
| 1600 ps_data = line.split() |
| 1601 if process_name in ps_data[-1]: |
| 1602 procs_pids[ps_data[-1]] = ps_data[1] |
| 1603 except IndexError: |
| 1604 pass |
| 1605 return procs_pids |
| 1606 |
| 1607 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1608 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): |
| 1609 """Takes a screenshot of the device. |
| 1610 |
| 1611 Args: |
| 1612 host_path: A string containing the path on the host to save the |
| 1613 screenshot to. If None, a file name in the current |
| 1614 directory will be generated. |
| 1615 timeout: timeout in seconds |
| 1616 retries: number of retries |
| 1617 |
| 1618 Returns: |
| 1619 The name of the file on the host to which the screenshot was saved. |
| 1620 |
| 1621 Raises: |
| 1622 CommandFailedError on failure. |
| 1623 CommandTimeoutError on timeout. |
| 1624 DeviceUnreachableError on missing device. |
| 1625 """ |
| 1626 if not host_path: |
| 1627 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp()) |
| 1628 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp: |
| 1629 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name], |
| 1630 check_return=True) |
| 1631 self.PullFile(device_tmp.name, host_path) |
| 1632 return host_path |
| 1633 |
| 1634 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1635 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None): |
| 1636 """Gets the memory usage for the given PID. |
| 1637 |
| 1638 Args: |
| 1639 pid: PID of the process. |
| 1640 timeout: timeout in seconds |
| 1641 retries: number of retries |
| 1642 |
| 1643 Returns: |
| 1644 A dict containing memory usage statistics for the PID. May include: |
| 1645 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean, |
| 1646 Private_Dirty, VmHWM |
| 1647 |
| 1648 Raises: |
| 1649 CommandTimeoutError on timeout. |
| 1650 """ |
| 1651 result = collections.defaultdict(int) |
| 1652 |
| 1653 try: |
| 1654 result.update(self._GetMemoryUsageForPidFromSmaps(pid)) |
| 1655 except device_errors.CommandFailedError: |
| 1656 logging.exception('Error getting memory usage from smaps') |
| 1657 |
| 1658 try: |
| 1659 result.update(self._GetMemoryUsageForPidFromStatus(pid)) |
| 1660 except device_errors.CommandFailedError: |
| 1661 logging.exception('Error getting memory usage from status') |
| 1662 |
| 1663 return result |
| 1664 |
| 1665 def _GetMemoryUsageForPidFromSmaps(self, pid): |
| 1666 SMAPS_COLUMNS = ( |
| 1667 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean', |
| 1668 'Private_Dirty') |
| 1669 |
| 1670 showmap_out = self._RunPipedShellCommand( |
| 1671 'showmap %d | grep TOTAL' % int(pid), as_root=True) |
| 1672 |
| 1673 split_totals = showmap_out[-1].split() |
| 1674 if (not split_totals |
| 1675 or len(split_totals) != 9 |
| 1676 or split_totals[-1] != 'TOTAL'): |
| 1677 raise device_errors.CommandFailedError( |
| 1678 'Invalid output from showmap: %s' % '\n'.join(showmap_out)) |
| 1679 |
| 1680 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals))) |
| 1681 |
| 1682 def _GetMemoryUsageForPidFromStatus(self, pid): |
| 1683 for line in self.ReadFile( |
| 1684 '/proc/%s/status' % str(pid), as_root=True).splitlines(): |
| 1685 if line.startswith('VmHWM:'): |
| 1686 return {'VmHWM': int(line.split()[1])} |
| 1687 else: |
| 1688 raise device_errors.CommandFailedError( |
| 1689 'Could not find memory peak value for pid %s', str(pid)) |
| 1690 |
| 1691 @decorators.WithTimeoutAndRetriesFromInstance() |
| 1692 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs): |
| 1693 """Returns a new LogcatMonitor associated with this device. |
| 1694 |
| 1695 Parameters passed to this function are passed directly to |
| 1696 |logcat_monitor.LogcatMonitor| and are documented there. |
| 1697 |
| 1698 Args: |
| 1699 timeout: timeout in seconds |
| 1700 retries: number of retries |
| 1701 """ |
| 1702 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) |
| 1703 |
| 1704 def GetClientCache(self, client_name): |
| 1705 """Returns client cache.""" |
| 1706 if client_name not in self._client_caches: |
| 1707 self._client_caches[client_name] = {} |
| 1708 return self._client_caches[client_name] |
| 1709 |
| 1710 def _ClearCache(self): |
| 1711 """Clears all caches.""" |
| 1712 for client in self._client_caches: |
| 1713 self._client_caches[client].clear() |
| 1714 self._cache.clear() |
| 1715 |
| 1716 @classmethod |
| 1717 def parallel(cls, devices=None, async=False): |
| 1718 """Creates a Parallelizer to operate over the provided list of devices. |
| 1719 |
| 1720 If |devices| is either |None| or an empty list, the Parallelizer will |
| 1721 operate over all attached devices that have not been blacklisted. |
| 1722 |
| 1723 Args: |
| 1724 devices: A list of either DeviceUtils instances or objects from |
| 1725 from which DeviceUtils instances can be constructed. If None, |
| 1726 all attached devices will be used. |
| 1727 async: If true, returns a Parallelizer that runs operations |
| 1728 asynchronously. |
| 1729 |
| 1730 Returns: |
| 1731 A Parallelizer operating over |devices|. |
| 1732 """ |
| 1733 if not devices: |
| 1734 devices = cls.HealthyDevices() |
| 1735 if not devices: |
| 1736 raise device_errors.NoDevicesError() |
| 1737 |
| 1738 devices = [d if isinstance(d, cls) else cls(d) for d in devices] |
| 1739 if async: |
| 1740 return parallelizer.Parallelizer(devices) |
| 1741 else: |
| 1742 return parallelizer.SyncParallelizer(devices) |
| 1743 |
| 1744 @classmethod |
| 1745 def HealthyDevices(cls): |
| 1746 blacklist = device_blacklist.ReadBlacklist() |
| 1747 def blacklisted(adb): |
| 1748 if adb.GetDeviceSerial() in blacklist: |
| 1749 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) |
| 1750 return True |
| 1751 return False |
| 1752 |
| 1753 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
| 1754 if not blacklisted(adb)] |
OLD | NEW |