Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 based on adb. | 5 """Provides a variety of device interactions based on adb. |
| 6 | 6 |
| 7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
| 8 """ | 8 """ |
| 9 # pylint: disable=W0613 | 9 # pylint: disable=W0613 |
| 10 | 10 |
| 11 import multiprocessing | |
| 12 import os | |
| 11 import pipes | 13 import pipes |
| 14 import shutil | |
| 12 import sys | 15 import sys |
| 16 import tempfile | |
| 13 import time | 17 import time |
| 18 import zipfile | |
| 14 | 19 |
| 15 import pylib.android_commands | 20 import pylib.android_commands |
| 16 from pylib.device import adb_wrapper | 21 from pylib.device import adb_wrapper |
| 17 from pylib.device import decorators | 22 from pylib.device import decorators |
| 18 from pylib.device import device_errors | 23 from pylib.device import device_errors |
| 24 from pylib.device.commands import install_commands | |
| 19 from pylib.utils import apk_helper | 25 from pylib.utils import apk_helper |
| 26 from pylib.utils import host_utils | |
| 20 from pylib.utils import parallelizer | 27 from pylib.utils import parallelizer |
| 21 | 28 |
| 22 _DEFAULT_TIMEOUT = 30 | 29 _DEFAULT_TIMEOUT = 30 |
| 23 _DEFAULT_RETRIES = 3 | 30 _DEFAULT_RETRIES = 3 |
| 24 | 31 |
| 25 | 32 |
| 26 @decorators.WithExplicitTimeoutAndRetries( | 33 @decorators.WithExplicitTimeoutAndRetries( |
| 27 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 34 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 28 def GetAVDs(): | 35 def GetAVDs(): |
| 29 """Returns a list of Android Virtual Devices. | 36 """Returns a list of Android Virtual Devices. |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 54 Args: | 61 Args: |
| 55 device: Either a device serial, an existing AdbWrapper instance, an | 62 device: Either a device serial, an existing AdbWrapper instance, an |
| 56 an existing AndroidCommands instance, or nothing. | 63 an existing AndroidCommands instance, or nothing. |
| 57 default_timeout: An integer containing the default number of seconds to | 64 default_timeout: An integer containing the default number of seconds to |
| 58 wait for an operation to complete if no explicit value | 65 wait for an operation to complete if no explicit value |
| 59 is provided. | 66 is provided. |
| 60 default_retries: An integer containing the default number or times an | 67 default_retries: An integer containing the default number or times an |
| 61 operation should be retried on failure if no explicit | 68 operation should be retried on failure if no explicit |
| 62 value is provided. | 69 value is provided. |
| 63 """ | 70 """ |
| 71 self.adb = None | |
| 64 self.old_interface = None | 72 self.old_interface = None |
| 65 if isinstance(device, basestring): | 73 if isinstance(device, basestring): |
| 74 self.adb = adb_wrapper.AdbWrapper(device) | |
| 66 self.old_interface = pylib.android_commands.AndroidCommands(device) | 75 self.old_interface = pylib.android_commands.AndroidCommands(device) |
| 67 elif isinstance(device, adb_wrapper.AdbWrapper): | 76 elif isinstance(device, adb_wrapper.AdbWrapper): |
| 77 self.adb = device | |
| 68 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 78 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
| 69 elif isinstance(device, pylib.android_commands.AndroidCommands): | 79 elif isinstance(device, pylib.android_commands.AndroidCommands): |
| 80 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) | |
| 70 self.old_interface = device | 81 self.old_interface = device |
| 71 elif not device: | 82 elif not device: |
| 83 self.adb = adb_wrapper.AdbWrapper('') | |
| 72 self.old_interface = pylib.android_commands.AndroidCommands() | 84 self.old_interface = pylib.android_commands.AndroidCommands() |
| 73 else: | 85 else: |
| 74 raise ValueError('Unsupported type passed for argument "device"') | 86 raise ValueError('Unsupported type passed for argument "device"') |
| 87 self._commands_installed = False | |
| 75 self._default_timeout = default_timeout | 88 self._default_timeout = default_timeout |
| 76 self._default_retries = default_retries | 89 self._default_retries = default_retries |
| 77 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) | 90 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) |
| 78 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) | 91 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) |
| 79 | 92 |
| 80 @decorators.WithTimeoutAndRetriesFromInstance() | 93 @decorators.WithTimeoutAndRetriesFromInstance() |
| 81 def IsOnline(self, timeout=None, retries=None): | 94 def IsOnline(self, timeout=None, retries=None): |
| 82 """Checks whether the device is online. | 95 """Checks whether the device is online. |
| 83 | 96 |
| 84 Args: | 97 Args: |
| 85 timeout: timeout in seconds | 98 timeout: timeout in seconds |
| 86 retries: number of retries | 99 retries: number of retries |
| 87 | 100 |
| 88 Returns: | 101 Returns: |
| 89 True if the device is online, False otherwise. | 102 True if the device is online, False otherwise. |
| 90 | 103 |
| 91 Raises: | 104 Raises: |
| 92 CommandTimeoutError on timeout. | 105 CommandTimeoutError on timeout. |
| 93 """ | 106 """ |
| 107 return self._IsOnlineImpl() | |
| 108 | |
| 109 def _IsOnlineImpl(self): | |
| 94 return self.old_interface.IsOnline() | 110 return self.old_interface.IsOnline() |
| 95 | 111 |
| 96 @decorators.WithTimeoutAndRetriesFromInstance() | 112 @decorators.WithTimeoutAndRetriesFromInstance() |
| 97 def HasRoot(self, timeout=None, retries=None): | 113 def HasRoot(self, timeout=None, retries=None): |
| 98 """Checks whether or not adbd has root privileges. | 114 """Checks whether or not adbd has root privileges. |
| 99 | 115 |
| 100 Args: | 116 Args: |
| 101 timeout: timeout in seconds | 117 timeout: timeout in seconds |
| 102 retries: number of retries | 118 retries: number of retries |
| 103 | 119 |
| 104 Returns: | 120 Returns: |
| 105 True if adbd has root privileges, False otherwise. | 121 True if adbd has root privileges, False otherwise. |
| 106 | 122 |
| 107 Raises: | 123 Raises: |
| 108 CommandTimeoutError on timeout. | 124 CommandTimeoutError on timeout. |
| 109 DeviceUnreachableError on missing device. | 125 DeviceUnreachableError on missing device. |
| 110 """ | 126 """ |
| 111 return self._HasRootImpl() | 127 return self._HasRootImpl() |
| 112 | 128 |
| 113 def _HasRootImpl(self): | 129 def _HasRootImpl(self): |
| 114 """Implementation of HasRoot. | |
| 115 | |
| 116 This is split from HasRoot to allow other DeviceUtils methods to call | |
| 117 HasRoot without spawning a new timeout thread. | |
| 118 | |
| 119 Returns: | |
| 120 Same as for |HasRoot|. | |
| 121 | |
| 122 Raises: | |
| 123 Same as for |HasRoot|. | |
| 124 """ | |
| 125 return self.old_interface.IsRootEnabled() | 130 return self.old_interface.IsRootEnabled() |
| 126 | 131 |
| 127 @decorators.WithTimeoutAndRetriesFromInstance() | 132 @decorators.WithTimeoutAndRetriesFromInstance() |
| 128 def EnableRoot(self, timeout=None, retries=None): | 133 def EnableRoot(self, timeout=None, retries=None): |
| 129 """Restarts adbd with root privileges. | 134 """Restarts adbd with root privileges. |
| 130 | 135 |
| 131 Args: | 136 Args: |
| 132 timeout: timeout in seconds | 137 timeout: timeout in seconds |
| 133 retries: number of retries | 138 retries: number of retries |
| 134 | 139 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 149 retries: number of retries | 154 retries: number of retries |
| 150 | 155 |
| 151 Returns: | 156 Returns: |
| 152 The device's path to its SD card. | 157 The device's path to its SD card. |
| 153 | 158 |
| 154 Raises: | 159 Raises: |
| 155 CommandFailedError if the external storage path could not be determined. | 160 CommandFailedError if the external storage path could not be determined. |
| 156 CommandTimeoutError on timeout. | 161 CommandTimeoutError on timeout. |
| 157 DeviceUnreachableError on missing device. | 162 DeviceUnreachableError on missing device. |
| 158 """ | 163 """ |
| 164 return self._GetExternalStoragePathImpl() | |
| 165 | |
| 166 def _GetExternalStoragePathImpl(self): | |
| 159 try: | 167 try: |
| 160 return self.old_interface.GetExternalStorage() | 168 return self.old_interface.GetExternalStorage() |
| 161 except AssertionError as e: | 169 except AssertionError as e: |
| 162 raise device_errors.CommandFailedError( | 170 raise device_errors.CommandFailedError( |
| 163 str(e), device=str(self)), None, sys.exc_info()[2] | 171 str(e), device=str(self)), None, sys.exc_info()[2] |
| 164 | 172 |
| 165 @decorators.WithTimeoutAndRetriesFromInstance() | 173 @decorators.WithTimeoutAndRetriesFromInstance() |
| 166 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 174 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| 167 """Wait for the device to fully boot. | 175 """Wait for the device to fully boot. |
| 168 | 176 |
| 169 This means waiting for the device to boot, the package manager to be | 177 This means waiting for the device to boot, the package manager to be |
| 170 available, and the SD card to be ready. It can optionally mean waiting | 178 available, and the SD card to be ready. It can optionally mean waiting |
| 171 for wifi to come up, too. | 179 for wifi to come up, too. |
| 172 | 180 |
| 173 Args: | 181 Args: |
| 174 wifi: A boolean indicating if we should wait for wifi to come up or not. | 182 wifi: A boolean indicating if we should wait for wifi to come up or not. |
| 175 timeout: timeout in seconds | 183 timeout: timeout in seconds |
| 176 retries: number of retries | 184 retries: number of retries |
| 177 | 185 |
| 178 Raises: | 186 Raises: |
| 179 CommandFailedError on failure. | 187 CommandFailedError on failure. |
| 180 CommandTimeoutError if one of the component waits times out. | 188 CommandTimeoutError if one of the component waits times out. |
| 181 DeviceUnreachableError if the device becomes unresponsive. | 189 DeviceUnreachableError if the device becomes unresponsive. |
| 182 """ | 190 """ |
| 183 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) | 191 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) |
| 184 | 192 |
| 185 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): | 193 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): |
| 186 """Implementation of WaitUntilFullyBooted. | |
| 187 | |
| 188 This is split from WaitUntilFullyBooted to allow other DeviceUtils methods | |
| 189 to call WaitUntilFullyBooted without spawning a new timeout thread. | |
| 190 | |
| 191 TODO(jbudorick) Remove the timeout parameter once this is no longer | |
| 192 implemented via AndroidCommands. | |
| 193 | |
| 194 Args: | |
| 195 wifi: Same as for |WaitUntilFullyBooted|. | |
| 196 timeout: timeout in seconds | |
| 197 | |
| 198 Raises: | |
| 199 Same as for |WaitUntilFullyBooted|. | |
| 200 """ | |
| 201 if timeout is None: | 194 if timeout is None: |
| 202 timeout = self._default_timeout | 195 timeout = self._default_timeout |
| 203 self.old_interface.WaitForSystemBootCompleted(timeout) | 196 self.old_interface.WaitForSystemBootCompleted(timeout) |
| 204 self.old_interface.WaitForDevicePm() | 197 self.old_interface.WaitForDevicePm() |
| 205 self.old_interface.WaitForSdCardReady(timeout) | 198 self.old_interface.WaitForSdCardReady(timeout) |
| 206 if wifi: | 199 if wifi: |
| 207 while not 'Wi-Fi is enabled' in ( | 200 while not 'Wi-Fi is enabled' in ( |
| 208 self._RunShellCommandImpl('dumpsys wifi')): | 201 self._RunShellCommandImpl('dumpsys wifi')): |
| 209 time.sleep(1) | 202 time.sleep(1) |
| 210 | 203 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 274 out = self.old_interface.Install(apk_path, reinstall=reinstall) | 267 out = self.old_interface.Install(apk_path, reinstall=reinstall) |
| 275 for line in out.splitlines(): | 268 for line in out.splitlines(): |
| 276 if 'Failure' in line: | 269 if 'Failure' in line: |
| 277 raise device_errors.CommandFailedError( | 270 raise device_errors.CommandFailedError( |
| 278 line.strip(), device=str(self)) | 271 line.strip(), device=str(self)) |
| 279 except AssertionError as e: | 272 except AssertionError as e: |
| 280 raise device_errors.CommandFailedError( | 273 raise device_errors.CommandFailedError( |
| 281 str(e), device=str(self)), None, sys.exc_info()[2] | 274 str(e), device=str(self)), None, sys.exc_info()[2] |
| 282 | 275 |
| 283 @decorators.WithTimeoutAndRetriesFromInstance() | 276 @decorators.WithTimeoutAndRetriesFromInstance() |
| 284 def RunShellCommand(self, cmd, check_return=False, as_root=False, | 277 def RunShellCommand(self, cmd, check_return=False, as_root=False, cwd=None, |
| 285 timeout=None, retries=None): | 278 env=None, timeout=None, retries=None): |
| 286 """Run an ADB shell command. | 279 """Run an ADB shell command. |
| 287 | 280 |
| 288 TODO(jbudorick) Switch the default value of check_return to True after | 281 TODO(jbudorick) Switch the default value of check_return to True after |
| 289 AndroidCommands is gone. | 282 AndroidCommands is gone. |
| 290 | 283 |
| 291 Args: | 284 Args: |
| 292 cmd: A list containing the command to run on the device and any arguments. | 285 cmd: A list containing the command to run on the device and any arguments. |
| 293 check_return: A boolean indicating whether or not the return code should | 286 check_return: A boolean indicating whether or not the return code should |
| 294 be checked. | 287 be checked. |
| 295 as_root: A boolean indicating whether the shell command should be run | 288 as_root: A boolean indicating whether the shell command should be run |
| 296 with root privileges. | 289 with root privileges. |
| 290 cwd: The device directory in which the command should be run. | |
| 291 env: The environment variables with which the command should be run. | |
| 297 timeout: timeout in seconds | 292 timeout: timeout in seconds |
| 298 retries: number of retries | 293 retries: number of retries |
| 299 | 294 |
| 300 Returns: | 295 Returns: |
| 301 The output of the command. | 296 The output of the command. |
| 302 | 297 |
| 303 Raises: | 298 Raises: |
| 304 CommandFailedError if check_return is True and the return code is nozero. | 299 CommandFailedError if check_return is True and the return code is nozero. |
| 305 CommandTimeoutError on timeout. | 300 CommandTimeoutError on timeout. |
| 306 DeviceUnreachableError on missing device. | 301 DeviceUnreachableError on missing device. |
| 307 """ | 302 """ |
| 308 return self._RunShellCommandImpl(cmd, check_return=check_return, | 303 return self._RunShellCommandImpl( |
| 309 as_root=as_root, timeout=timeout) | 304 cmd, check_return=check_return, as_root=as_root, cwd=cwd, env=env, |
| 305 timeout=timeout) | |
| 310 | 306 |
| 311 def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False, | 307 def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False, |
| 312 timeout=None): | 308 cwd=None, env=None, timeout=None): |
| 313 """Implementation of RunShellCommand. | 309 # TODO(jbudorick): Remove the timeout parameter once this is no longer |
| 314 | 310 # backed by AndroidCommands. |
| 315 This is split from RunShellCommand to allow other DeviceUtils methods to | |
| 316 call RunShellCommand without spawning a new timeout thread. | |
| 317 | |
| 318 TODO(jbudorick) Remove the timeout parameter once this is no longer | |
| 319 implemented via AndroidCommands. | |
| 320 | |
| 321 Args: | |
| 322 cmd: Same as for |RunShellCommand|. | |
| 323 check_return: Same as for |RunShellCommand|. | |
| 324 as_root: Same as for |RunShellCommand|. | |
| 325 timeout: timeout in seconds | |
| 326 | |
| 327 Raises: | |
| 328 Same as for |RunShellCommand|. | |
| 329 | |
| 330 Returns: | |
| 331 Same as for |RunShellCommand|. | |
| 332 """ | |
| 333 if isinstance(cmd, list): | 311 if isinstance(cmd, list): |
| 334 cmd = ' '.join(cmd) | 312 cmd = ' '.join(cmd) |
| 335 if as_root and not self.HasRoot(): | 313 if as_root and not self._HasRootImpl(): |
| 336 cmd = 'su -c %s' % cmd | 314 cmd = 'su -c %s' % cmd |
| 315 if env: | |
| 316 cmd = '%s %s' % ( | |
| 317 ' '.join('%s=%s' % (k, v) for k, v in env.iteritems()), cmd) | |
| 318 if cwd: | |
| 319 cmd = 'cd %s && %s' % (cwd, cmd) | |
| 337 if check_return: | 320 if check_return: |
| 338 code, output = self.old_interface.GetShellCommandStatusAndOutput( | 321 code, output = self.old_interface.GetShellCommandStatusAndOutput( |
| 339 cmd, timeout_time=timeout) | 322 cmd, timeout_time=timeout) |
| 340 if int(code) != 0: | 323 if int(code) != 0: |
| 341 raise device_errors.AdbCommandFailedError( | 324 raise device_errors.AdbCommandFailedError( |
| 342 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self)) | 325 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self)) |
| 343 else: | 326 else: |
| 344 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) | 327 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) |
| 345 return output | 328 return output |
| 346 | 329 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 494 DeviceUnreachableError on missing device. | 477 DeviceUnreachableError on missing device. |
| 495 """ | 478 """ |
| 496 self.old_interface.SendKeyEvent(keycode) | 479 self.old_interface.SendKeyEvent(keycode) |
| 497 | 480 |
| 498 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 481 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
| 499 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES | 482 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 500 | 483 |
| 501 @decorators.WithTimeoutAndRetriesDefaults( | 484 @decorators.WithTimeoutAndRetriesDefaults( |
| 502 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, | 485 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, |
| 503 PUSH_CHANGED_FILES_DEFAULT_RETRIES) | 486 PUSH_CHANGED_FILES_DEFAULT_RETRIES) |
| 504 def PushChangedFiles(self, host_path, device_path, timeout=None, | 487 def PushChangedFiles(self, host_device_tuples, timeout=None, |
| 505 retries=None): | 488 retries=None): |
| 506 """Push files to the device, skipping files that don't need updating. | 489 """Push files to the device, skipping files that don't need updating. |
| 507 | 490 |
| 508 Args: | 491 Args: |
| 509 host_path: A string containing the absolute path to the file or directory | 492 host_device_tuples: A list of (host_path, device_path) tuples, where |
| 510 on the host that should be minimally pushed to the device. | 493 |host_path| is an absolute path of a file or directory on the host |
| 511 device_path: A string containing the absolute path of the destination on | 494 that should be minimially pushed to the device, and |device_path| is |
| 512 the device. | 495 an absolute path of the destination on the device. |
| 513 timeout: timeout in seconds | 496 timeout: timeout in seconds |
| 514 retries: number of retries | 497 retries: number of retries |
| 515 | 498 |
| 516 Raises: | 499 Raises: |
| 517 CommandFailedError on failure. | 500 CommandFailedError on failure. |
| 518 CommandTimeoutError on timeout. | 501 CommandTimeoutError on timeout. |
| 519 DeviceUnreachableError on missing device. | 502 DeviceUnreachableError on missing device. |
| 520 """ | 503 """ |
| 521 self.old_interface.PushIfNeeded(host_path, device_path) | 504 |
| 505 files = [] | |
| 506 for h, d in host_device_tuples: | |
| 507 if os.path.isdir(h) and not self._FileExistsImpl(d): | |
| 508 self._RunShellCommandImpl(['mkdir', '-p', '"%s"' % d], | |
|
cjhopman
2014/10/09 17:50:23
It's may be a little faster to just unconditionall
jbudorick
2014/10/09 19:26:40
dropped the _FileExistsImpl call
| |
| 509 check_return=True) | |
| 510 files += self.old_interface.GetFilesChanged(h, d) | |
| 511 | |
| 512 if not files: | |
| 513 return | |
| 514 | |
| 515 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | |
| 516 file_count = len(files) | |
| 517 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | |
| 518 for h, _ in host_device_tuples) | |
| 519 dir_file_count = 0 | |
| 520 for h, _ in host_device_tuples: | |
| 521 if os.path.isdir(h): | |
| 522 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | |
| 523 else: | |
| 524 dir_file_count += 1 | |
| 525 | |
| 526 push_duration = self._ApproximateDuration( | |
| 527 file_count, file_count, size, False) | |
| 528 dir_push_duration = self._ApproximateDuration( | |
| 529 len(host_device_tuples), dir_file_count, dir_size, False) | |
| 530 zip_duration = self._ApproximateDuration(1, 1, size, True) | |
| 531 | |
| 532 if dir_push_duration < push_duration and dir_push_duration < zip_duration: | |
| 533 self._PushChangedFilesIndividually(host_device_tuples) | |
| 534 elif push_duration < zip_duration: | |
| 535 self._PushChangedFilesIndividually(files) | |
| 536 else: | |
| 537 self._PushChangedFilesZipped(files) | |
| 538 self._RunShellCommandImpl( | |
| 539 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | |
| 540 as_root=True) | |
| 541 | |
| 542 @staticmethod | |
| 543 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): | |
| 544 # We approximate the time to push a set of files to a device as: | |
| 545 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where | |
| 546 # t: total time (sec) | |
| 547 # c1: adb call time delay (sec) | |
| 548 # a: number of times adb is called (unitless) | |
| 549 # c2: push time delay (sec) | |
| 550 # f: number of files pushed via adb (unitless) | |
| 551 # c3: zip time delay (sec) | |
| 552 # c4: zip rate (bytes/sec) | |
| 553 # b: total number of bytes (bytes) | |
| 554 # c5: transfer rate (bytes/sec) | |
| 555 # c6: compression ratio (unitless) | |
| 556 | |
| 557 # All of these are approximations. | |
| 558 ADB_CALL_PENALTY = 0.1 # seconds | |
| 559 ADB_PUSH_PENALTY = 0.01 # seconds | |
| 560 ZIP_PENALTY = 2.0 # seconds | |
| 561 ZIP_RATE = 10000000.0 # bytes / second | |
| 562 TRANSFER_RATE = 2000000.0 # bytes / second | |
| 563 COMPRESSION_RATIO = 2.0 # unitless | |
| 564 | |
| 565 adb_call_time = ADB_CALL_PENALTY * adb_calls | |
| 566 adb_push_setup_time = ADB_PUSH_PENALTY * file_count | |
| 567 if is_zipping: | |
| 568 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE | |
| 569 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO) | |
| 570 else: | |
| 571 zip_time = 0 | |
| 572 transfer_time = byte_count / TRANSFER_RATE | |
| 573 return (adb_call_time + adb_push_setup_time + zip_time + transfer_time) | |
| 574 | |
| 575 def _PushChangedFilesIndividually(self, files): | |
| 576 for h, d in files: | |
| 577 self.adb.Push(h, d) | |
| 578 | |
| 579 def _PushChangedFilesZipped(self, files): | |
| 580 if not files: | |
| 581 return | |
| 582 | |
| 583 self._InstallCommands() | |
| 584 zip_dir = tempfile.mkdtemp() | |
|
cjhopman
2014/10/09 17:50:23
this can use tempfile.NamedTemporaryFile for the z
jbudorick
2014/10/09 19:26:40
What I actually want is a temporary file _name_, n
cjhopman
2014/10/09 20:27:39
You can actually do:
with tempfile.NamedTemporary
jbudorick
2014/10/10 09:33:47
I was thinking python would choke on having the sa
| |
| 585 try: | |
| 586 zip_path = os.path.abspath(os.path.join(zip_dir, 'tmp.zip')) | |
| 587 | |
| 588 zip_proc = multiprocessing.Process( | |
| 589 target=DeviceUtils._CreateDeviceZip, | |
| 590 args=(zip_path, files)) | |
| 591 zip_proc.start() | |
| 592 zip_proc.join() | |
| 593 | |
| 594 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() | |
| 595 try: | |
| 596 self.adb.Push(zip_path, zip_on_device) | |
| 597 self._RunShellCommandImpl( | |
| 598 ['unzip', zip_on_device], | |
| 599 as_root=True, check_return=True, | |
| 600 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}) | |
| 601 finally: | |
| 602 if zip_proc.is_alive(): | |
| 603 zip_proc.terminate() | |
| 604 if self._IsOnlineImpl(): | |
| 605 self._RunShellCommandImpl(['rm', zip_on_device]) | |
| 606 finally: | |
| 607 shutil.rmtree(zip_dir) | |
| 608 | |
| 609 def _InstallCommands(self): | |
| 610 if not self._commands_installed and not install_commands.Installed(self): | |
| 611 install_commands.InstallCommands(self) | |
| 612 self._commands_installed = True | |
| 613 | |
| 614 @staticmethod | |
| 615 def _CreateDeviceZip(zip_path, host_device_tuples): | |
| 616 with zipfile.ZipFile(zip_path, 'w') as zip_file: | |
| 617 def AddDirectory(z, h, d): | |
| 618 z.write(h, d, zipfile.ZIP_STORED) | |
| 619 for i in os.listdir(h): | |
| 620 hi = os.path.join(h, i) | |
| 621 di = '%s/%s' % (d, i) | |
| 622 if os.path.isdir(i): | |
| 623 AddDirectory(z, hi, di) | |
| 624 else: | |
| 625 z.write(hi, di, zipfile.ZIP_DEFLATED) | |
| 626 | |
| 627 for host_path, device_path in host_device_tuples: | |
|
cjhopman
2014/10/09 17:50:23
I think this would be simpler with os.walk.
somet
jbudorick
2014/10/09 19:26:40
Done with some tweaks.
| |
| 628 if os.path.isdir(host_path): | |
| 629 AddDirectory(zip_file, host_path, device_path) | |
| 630 else: | |
| 631 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) | |
| 522 | 632 |
| 523 @decorators.WithTimeoutAndRetriesFromInstance() | 633 @decorators.WithTimeoutAndRetriesFromInstance() |
| 524 def FileExists(self, device_path, timeout=None, retries=None): | 634 def FileExists(self, device_path, timeout=None, retries=None): |
| 525 """Checks whether the given file exists on the device. | 635 """Checks whether the given file exists on the device. |
| 526 | 636 |
| 527 Args: | 637 Args: |
| 528 device_path: A string containing the absolute path to the file on the | 638 device_path: A string containing the absolute path to the file on the |
| 529 device. | 639 device. |
| 530 timeout: timeout in seconds | 640 timeout: timeout in seconds |
| 531 retries: number of retries | 641 retries: number of retries |
| 532 | 642 |
| 533 Returns: | 643 Returns: |
| 534 True if the file exists on the device, False otherwise. | 644 True if the file exists on the device, False otherwise. |
| 535 | 645 |
| 536 Raises: | 646 Raises: |
| 537 CommandTimeoutError on timeout. | 647 CommandTimeoutError on timeout. |
| 538 DeviceUnreachableError on missing device. | 648 DeviceUnreachableError on missing device. |
| 539 """ | 649 """ |
| 540 return self._FileExistsImpl(device_path) | 650 return self._FileExistsImpl(device_path) |
| 541 | 651 |
| 542 def _FileExistsImpl(self, device_path): | 652 def _FileExistsImpl(self, device_path): |
| 543 """Implementation of FileExists. | |
| 544 | |
| 545 This is split from FileExists to allow other DeviceUtils methods to call | |
| 546 FileExists without spawning a new timeout thread. | |
| 547 | |
| 548 Args: | |
| 549 device_path: Same as for |FileExists|. | |
| 550 | |
| 551 Returns: | |
| 552 True if the file exists on the device, False otherwise. | |
| 553 | |
| 554 Raises: | |
| 555 Same as for |FileExists|. | |
| 556 """ | |
| 557 return self.old_interface.FileExistsOnDevice(device_path) | 653 return self.old_interface.FileExistsOnDevice(device_path) |
| 558 | 654 |
| 559 @decorators.WithTimeoutAndRetriesFromInstance() | 655 @decorators.WithTimeoutAndRetriesFromInstance() |
| 560 def PullFile(self, device_path, host_path, timeout=None, retries=None): | 656 def PullFile(self, device_path, host_path, timeout=None, retries=None): |
| 561 """Pull a file from the device. | 657 """Pull a file from the device. |
| 562 | 658 |
| 563 Args: | 659 Args: |
| 564 device_path: A string containing the absolute path of the file to pull | 660 device_path: A string containing the absolute path of the file to pull |
| 565 from the device. | 661 from the device. |
| 566 host_path: A string containing the absolute path of the destination on | 662 host_path: A string containing the absolute path of the destination on |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 591 retries: number of retries | 687 retries: number of retries |
| 592 | 688 |
| 593 Returns: | 689 Returns: |
| 594 The contents of the file at |device_path| as a list of lines. | 690 The contents of the file at |device_path| as a list of lines. |
| 595 | 691 |
| 596 Raises: | 692 Raises: |
| 597 CommandFailedError if the file can't be read. | 693 CommandFailedError if the file can't be read. |
| 598 CommandTimeoutError on timeout. | 694 CommandTimeoutError on timeout. |
| 599 DeviceUnreachableError on missing device. | 695 DeviceUnreachableError on missing device. |
| 600 """ | 696 """ |
| 601 # TODO(jbudorick) Evaluate whether we awant to return a list of lines after | 697 # TODO(jbudorick) Evaluate whether we want to return a list of lines after |
| 602 # the implementation switch, and if file not found should raise exception. | 698 # the implementation switch, and if file not found should raise exception. |
| 603 if as_root: | 699 if as_root: |
| 604 if not self.old_interface.CanAccessProtectedFileContents(): | 700 if not self.old_interface.CanAccessProtectedFileContents(): |
| 605 raise device_errors.CommandFailedError( | 701 raise device_errors.CommandFailedError( |
| 606 'Cannot read from %s with root privileges.' % device_path) | 702 'Cannot read from %s with root privileges.' % device_path) |
| 607 return self.old_interface.GetProtectedFileContents(device_path) | 703 return self.old_interface.GetProtectedFileContents(device_path) |
| 608 else: | 704 else: |
| 609 return self.old_interface.GetFileContents(device_path) | 705 return self.old_interface.GetFileContents(device_path) |
| 610 | 706 |
| 611 @decorators.WithTimeoutAndRetriesFromInstance() | 707 @decorators.WithTimeoutAndRetriesFromInstance() |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 748 A dict mapping process name to PID for each process that contained the | 844 A dict mapping process name to PID for each process that contained the |
| 749 provided |process_name|. | 845 provided |process_name|. |
| 750 | 846 |
| 751 Raises: | 847 Raises: |
| 752 CommandTimeoutError on timeout. | 848 CommandTimeoutError on timeout. |
| 753 DeviceUnreachableError on missing device. | 849 DeviceUnreachableError on missing device. |
| 754 """ | 850 """ |
| 755 return self._GetPidsImpl(process_name) | 851 return self._GetPidsImpl(process_name) |
| 756 | 852 |
| 757 def _GetPidsImpl(self, process_name): | 853 def _GetPidsImpl(self, process_name): |
| 758 """Implementation of GetPids. | |
| 759 | |
| 760 This is split from GetPids to allow other DeviceUtils methods to call | |
| 761 GetPids without spawning a new timeout thread. | |
| 762 | |
| 763 Args: | |
| 764 process_name: A string containing the process name to get the PIDs for. | |
| 765 | |
| 766 Returns: | |
| 767 A dict mapping process name to PID for each process that contained the | |
| 768 provided |process_name|. | |
| 769 | |
| 770 Raises: | |
| 771 DeviceUnreachableError on missing device. | |
| 772 """ | |
| 773 procs_pids = {} | 854 procs_pids = {} |
| 774 for line in self._RunShellCommandImpl('ps'): | 855 for line in self._RunShellCommandImpl('ps'): |
| 775 try: | 856 try: |
| 776 ps_data = line.split() | 857 ps_data = line.split() |
| 777 if process_name in ps_data[-1]: | 858 if process_name in ps_data[-1]: |
| 778 procs_pids[ps_data[-1]] = ps_data[1] | 859 procs_pids[ps_data[-1]] = ps_data[1] |
| 779 except IndexError: | 860 except IndexError: |
| 780 pass | 861 pass |
| 781 return procs_pids | 862 return procs_pids |
| 782 | 863 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 863 A Parallelizer operating over |devices|. | 944 A Parallelizer operating over |devices|. |
| 864 """ | 945 """ |
| 865 if not devices or len(devices) == 0: | 946 if not devices or len(devices) == 0: |
| 866 devices = pylib.android_commands.GetAttachedDevices() | 947 devices = pylib.android_commands.GetAttachedDevices() |
| 867 parallelizer_type = (parallelizer.Parallelizer if async | 948 parallelizer_type = (parallelizer.Parallelizer if async |
| 868 else parallelizer.SyncParallelizer) | 949 else parallelizer.SyncParallelizer) |
| 869 return parallelizer_type([ | 950 return parallelizer_type([ |
| 870 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 951 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
| 871 for d in devices]) | 952 for d in devices]) |
| 872 | 953 |
| OLD | NEW |