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 |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 119 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 119 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
| 120 elif isinstance(device, pylib.android_commands.AndroidCommands): | 120 elif isinstance(device, pylib.android_commands.AndroidCommands): |
| 121 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) | 121 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) |
| 122 self.old_interface = device | 122 self.old_interface = device |
| 123 else: | 123 else: |
| 124 raise ValueError('Unsupported device value: %r' % device) | 124 raise ValueError('Unsupported device value: %r' % device) |
| 125 self._commands_installed = None | 125 self._commands_installed = None |
| 126 self._default_timeout = default_timeout | 126 self._default_timeout = default_timeout |
| 127 self._default_retries = default_retries | 127 self._default_retries = default_retries |
| 128 self._cache = {} | 128 self._cache = {} |
| 129 self._cache['application_paths'] = {} | |
| 129 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) | 130 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) |
| 130 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) | 131 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) |
| 131 | 132 |
| 132 @decorators.WithTimeoutAndRetriesFromInstance() | 133 @decorators.WithTimeoutAndRetriesFromInstance() |
| 133 def IsOnline(self, timeout=None, retries=None): | 134 def IsOnline(self, timeout=None, retries=None): |
| 134 """Checks whether the device is online. | 135 """Checks whether the device is online. |
| 135 | 136 |
| 136 Args: | 137 Args: |
| 137 timeout: timeout in seconds | 138 timeout: timeout in seconds |
| 138 retries: number of retries | 139 retries: number of retries |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 272 Args: | 273 Args: |
| 273 package: Name of the package. | 274 package: Name of the package. |
| 274 | 275 |
| 275 Returns: | 276 Returns: |
| 276 Path to the apk on the device if it exists, None otherwise. | 277 Path to the apk on the device if it exists, None otherwise. |
| 277 """ | 278 """ |
| 278 # 'pm path' is liable to incorrectly exit with a nonzero number starting | 279 # 'pm path' is liable to incorrectly exit with a nonzero number starting |
| 279 # in Lollipop. | 280 # in Lollipop. |
| 280 # TODO(jbudorick): Check if this is fixed as new Android versions are | 281 # TODO(jbudorick): Check if this is fixed as new Android versions are |
| 281 # released to put an upper bound on this. | 282 # released to put an upper bound on this. |
| 283 application_paths = self._cache['application_paths'] | |
|
jbudorick
2015/01/21 22:54:09
If we stay with the cache, please move this above
| |
| 284 if package in application_paths: | |
| 285 return application_paths[package] | |
| 286 | |
| 282 should_check_return = (self.build_version_sdk < | 287 should_check_return = (self.build_version_sdk < |
|
jbudorick
2015/01/21 22:54:09
w.r.t. my tuning suggestion: we already have some
| |
| 283 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 288 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) |
| 284 output = self.RunShellCommand(['pm', 'path', package], single_line=True, | 289 output = self.RunShellCommand(['pm', 'path', package], single_line=True, |
| 285 check_return=should_check_return) | 290 check_return=should_check_return) |
| 286 if not output: | 291 if not output: |
| 287 return None | 292 return None |
| 288 if not output.startswith('package:'): | 293 if not output.startswith('package:'): |
| 289 raise device_errors.CommandFailedError('pm path returned: %r' % output, | 294 raise device_errors.CommandFailedError('pm path returned: %r' % output, |
| 290 str(self)) | 295 str(self)) |
| 291 return output[len('package:'):] | 296 application_paths[package] = output[len('package:'):] |
| 297 return application_paths[package] | |
| 292 | 298 |
| 293 @decorators.WithTimeoutAndRetriesFromInstance() | 299 @decorators.WithTimeoutAndRetriesFromInstance() |
| 294 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 300 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
| 295 """Wait for the device to fully boot. | 301 """Wait for the device to fully boot. |
| 296 | 302 |
| 297 This means waiting for the device to boot, the package manager to be | 303 This means waiting for the device to boot, the package manager to be |
| 298 available, and the SD card to be ready. It can optionally mean waiting | 304 available, and the SD card to be ready. It can optionally mean waiting |
| 299 for wifi to come up, too. | 305 for wifi to come up, too. |
| 300 | 306 |
| 301 Args: | 307 Args: |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 352 | 358 |
| 353 Raises: | 359 Raises: |
| 354 CommandTimeoutError on timeout. | 360 CommandTimeoutError on timeout. |
| 355 DeviceUnreachableError on missing device. | 361 DeviceUnreachableError on missing device. |
| 356 """ | 362 """ |
| 357 def device_offline(): | 363 def device_offline(): |
| 358 return not self.IsOnline() | 364 return not self.IsOnline() |
| 359 | 365 |
| 360 self.adb.Reboot() | 366 self.adb.Reboot() |
| 361 self._cache = {} | 367 self._cache = {} |
| 368 self._cache['application_paths'] = {} | |
| 362 timeout_retry.WaitFor(device_offline, wait_period=1) | 369 timeout_retry.WaitFor(device_offline, wait_period=1) |
| 363 if block: | 370 if block: |
| 364 self.WaitUntilFullyBooted() | 371 self.WaitUntilFullyBooted() |
| 365 | 372 |
| 366 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT | 373 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT |
| 367 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES | 374 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 368 | 375 |
| 369 @decorators.WithTimeoutAndRetriesDefaults( | 376 @decorators.WithTimeoutAndRetriesDefaults( |
| 370 INSTALL_DEFAULT_TIMEOUT, | 377 INSTALL_DEFAULT_TIMEOUT, |
| 371 INSTALL_DEFAULT_RETRIES) | 378 INSTALL_DEFAULT_RETRIES) |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 389 device_path = self.GetApplicationPath(package_name) | 396 device_path = self.GetApplicationPath(package_name) |
| 390 if device_path is not None: | 397 if device_path is not None: |
| 391 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) | 398 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) |
| 392 if should_install and not reinstall: | 399 if should_install and not reinstall: |
| 393 self.adb.Uninstall(package_name) | 400 self.adb.Uninstall(package_name) |
| 394 else: | 401 else: |
| 395 should_install = True | 402 should_install = True |
| 396 if should_install: | 403 if should_install: |
| 397 self.adb.Install(apk_path, reinstall=reinstall) | 404 self.adb.Install(apk_path, reinstall=reinstall) |
| 398 | 405 |
| 406 application_paths = self._cache['application_paths'] | |
| 407 if package_name in application_paths: | |
| 408 del application_paths[package_name] | |
| 409 | |
| 399 @decorators.WithTimeoutAndRetriesFromInstance() | 410 @decorators.WithTimeoutAndRetriesFromInstance() |
| 400 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 411 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
| 401 as_root=False, single_line=False, | 412 as_root=False, single_line=False, |
| 402 timeout=None, retries=None): | 413 timeout=None, retries=None): |
| 403 """Run an ADB shell command. | 414 """Run an ADB shell command. |
| 404 | 415 |
| 405 The command to run |cmd| should be a sequence of program arguments or else | 416 The command to run |cmd| should be a sequence of program arguments or else |
| 406 a single string. | 417 a single string. |
| 407 | 418 |
| 408 When |cmd| is a sequence, it is assumed to contain the name of the command | 419 When |cmd| is a sequence, it is assumed to contain the name of the command |
| 409 to run followed by its arguments. In this case, arguments are passed to the | 420 to run followed by its arguments. In this case, arguments are passed to the |
| 410 command exactly as given, without any further processing by the shell. This | 421 command exactly as given, without any further processing by the shell. This |
| 411 allows to easily pass arguments containing spaces or special characters | 422 allows to easily pass arguments containing spaces or special characters |
| 412 without having to worry about getting quoting right. Whenever possible, it | 423 without having to worry about getting quoting right. Whenever possible, it |
| 413 is recomended to pass |cmd| as a sequence. | 424 is recomended to pass |cmd| as a sequence. |
| 414 | 425 |
| 415 When |cmd| is given as a string, it will be interpreted and run by the | 426 When |cmd| is given as a string, it will be interpreted and run by the |
| 416 shell on the device. | 427 shell on the device. |
| 417 | 428 |
| 418 This behaviour is consistent with that of command runners in cmd_helper as | 429 This behaviour is consistent with that of command runners in cmd_helper as |
| 419 well as Python's own subprocess.Popen. | 430 well as Python's own subprocess.Popen. |
| 420 | 431 |
| 421 TODO(perezju) Change the default of |check_return| to True when callers | 432 TODO(perezju) Change the default of |check_return| to True when callers |
| 422 have switched to the new behaviour. | 433 have switched to the new behaviour. |
| 423 | 434 |
| 435 TODO(jaekyun) Should clear some of non-RO cache items if this command | |
| 436 affects them. | |
| 437 | |
| 424 Args: | 438 Args: |
| 425 cmd: A string with the full command to run on the device, or a sequence | 439 cmd: A string with the full command to run on the device, or a sequence |
| 426 containing the command and its arguments. | 440 containing the command and its arguments. |
| 427 check_return: A boolean indicating whether or not the return code should | 441 check_return: A boolean indicating whether or not the return code should |
| 428 be checked. | 442 be checked. |
| 429 cwd: The device directory in which the command should be run. | 443 cwd: The device directory in which the command should be run. |
| 430 env: The environment variables with which the command should be run. | 444 env: The environment variables with which the command should be run. |
| 431 as_root: A boolean indicating whether the shell command should be run | 445 as_root: A boolean indicating whether the shell command should be run |
| 432 with root privileges. | 446 with root privileges. |
| 433 single_line: A boolean indicating if only a single line of output is | 447 single_line: A boolean indicating if only a single line of output is |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 449 DeviceUnreachableError on missing device. | 463 DeviceUnreachableError on missing device. |
| 450 """ | 464 """ |
| 451 def env_quote(key, value): | 465 def env_quote(key, value): |
| 452 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 466 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
| 453 raise KeyError('Invalid shell variable name %r' % key) | 467 raise KeyError('Invalid shell variable name %r' % key) |
| 454 # using double quotes here to allow interpolation of shell variables | 468 # using double quotes here to allow interpolation of shell variables |
| 455 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 469 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
| 456 | 470 |
| 457 if not isinstance(cmd, basestring): | 471 if not isinstance(cmd, basestring): |
| 458 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 472 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
| 473 | |
| 474 if cmd.startswith('pm install') or cmd.startswith('pm uninstall'): | |
|
jbudorick
2015/01/21 22:54:09
O_O
This looks _extremely_ dangerous / flaky / te
| |
| 475 self._cache['application_paths'] = {} | |
| 476 | |
| 459 if env: | 477 if env: |
| 460 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 478 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
| 461 cmd = '%s %s' % (env, cmd) | 479 cmd = '%s %s' % (env, cmd) |
| 462 if cwd: | 480 if cwd: |
| 463 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 481 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
| 464 if as_root and self.NeedsSU(): | 482 if as_root and self.NeedsSU(): |
| 465 # "su -c sh -c" allows using shell features in |cmd| | 483 # "su -c sh -c" allows using shell features in |cmd| |
| 466 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) | 484 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
| 467 if timeout is None: | 485 if timeout is None: |
| 468 timeout = self._default_timeout | 486 timeout = self._default_timeout |
| (...skipping 816 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1285 Returns: | 1303 Returns: |
| 1286 A Parallelizer operating over |devices|. | 1304 A Parallelizer operating over |devices|. |
| 1287 """ | 1305 """ |
| 1288 if not devices: | 1306 if not devices: |
| 1289 devices = adb_wrapper.AdbWrapper.GetDevices() | 1307 devices = adb_wrapper.AdbWrapper.GetDevices() |
| 1290 devices = [d if isinstance(d, cls) else cls(d) for d in devices] | 1308 devices = [d if isinstance(d, cls) else cls(d) for d in devices] |
| 1291 if async: | 1309 if async: |
| 1292 return parallelizer.Parallelizer(devices) | 1310 return parallelizer.Parallelizer(devices) |
| 1293 else: | 1311 else: |
| 1294 return parallelizer.SyncParallelizer(devices) | 1312 return parallelizer.SyncParallelizer(devices) |
| OLD | NEW |