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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 """Restarts the adb server. | 67 """Restarts the adb server. |
| 68 | 68 |
| 69 Raises: | 69 Raises: |
| 70 CommandFailedError if we fail to kill or restart the server. | 70 CommandFailedError if we fail to kill or restart the server. |
| 71 """ | 71 """ |
| 72 pylib.android_commands.AndroidCommands().RestartAdbServer() | 72 pylib.android_commands.AndroidCommands().RestartAdbServer() |
| 73 | 73 |
| 74 | 74 |
| 75 class DeviceUtils(object): | 75 class DeviceUtils(object): |
| 76 | 76 |
| 77 _MAX_ADB_COMMAND_LENGTH = 512 | |
| 77 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 78 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
| 78 | 79 |
| 79 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 80 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
| 80 default_retries=_DEFAULT_RETRIES): | 81 default_retries=_DEFAULT_RETRIES): |
| 81 """DeviceUtils constructor. | 82 """DeviceUtils constructor. |
| 82 | 83 |
| 83 Args: | 84 Args: |
| 84 device: Either a device serial, an existing AdbWrapper instance, or an | 85 device: Either a device serial, an existing AdbWrapper instance, or an |
| 85 an existing AndroidCommands instance. | 86 an existing AndroidCommands instance. |
| 86 default_timeout: An integer containing the default number of seconds to | 87 default_timeout: An integer containing the default number of seconds to |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 428 more lines. | 429 more lines. |
| 429 CommandTimeoutError on timeout. | 430 CommandTimeoutError on timeout. |
| 430 DeviceUnreachableError on missing device. | 431 DeviceUnreachableError on missing device. |
| 431 """ | 432 """ |
| 432 def env_quote(key, value): | 433 def env_quote(key, value): |
| 433 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 434 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
| 434 raise KeyError('Invalid shell variable name %r' % key) | 435 raise KeyError('Invalid shell variable name %r' % key) |
| 435 # using double quotes here to allow interpolation of shell variables | 436 # using double quotes here to allow interpolation of shell variables |
| 436 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 437 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
| 437 | 438 |
| 439 def do_run(cmd): | |
| 440 try: | |
| 441 return self.adb.Shell(cmd) | |
| 442 except device_errors.AdbCommandFailedError as exc: | |
| 443 if check_return: | |
| 444 raise | |
| 445 else: | |
| 446 return exc.output | |
| 447 | |
| 438 if not isinstance(cmd, basestring): | 448 if not isinstance(cmd, basestring): |
| 439 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 449 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
| 440 if env: | 450 if env: |
| 441 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 451 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
| 442 cmd = '%s %s' % (env, cmd) | 452 cmd = '%s %s' % (env, cmd) |
| 443 if cwd: | 453 if cwd: |
| 444 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 454 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
| 445 if as_root and self.NeedsSU(): | 455 if as_root and self.NeedsSU(): |
| 446 # "su -c sh -c" allows using shell features in |cmd| | 456 # "su -c sh -c" allows using shell features in |cmd| |
| 447 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) | 457 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
| 448 if timeout is None: | 458 if timeout is None: |
| 449 timeout = self._default_timeout | 459 timeout = self._default_timeout |
| 450 | 460 |
| 451 try: | 461 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: |
| 452 output = self.adb.Shell(cmd) | 462 output = do_run(cmd) |
| 453 except device_errors.AdbCommandFailedError as e: | 463 else: |
| 454 if check_return: | 464 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
| 455 raise | 465 self._WriteFileWithPush(script.name, cmd) |
| 456 else: | 466 logging.info('Large shell command will be run from file: %s ...', |
| 457 output = e.output | 467 cmd[:100]) |
|
perezju
2014/12/15 15:48:59
I've noted that running large shell commands in th
jbudorick
2014/12/15 23:18:28
sgtm
| |
| 468 output = do_run('sh %s' % script.name_quoted) | |
| 458 | 469 |
| 459 output = output.splitlines() | 470 output = output.splitlines() |
| 460 if single_line: | 471 if single_line: |
| 461 if not output: | 472 if not output: |
| 462 return '' | 473 return '' |
| 463 elif len(output) == 1: | 474 elif len(output) == 1: |
| 464 return output[0] | 475 return output[0] |
| 465 else: | 476 else: |
| 466 msg = 'one line of output was expected, but got: %s' | 477 msg = 'one line of output was expected, but got: %s' |
| 467 raise device_errors.CommandFailedError(msg % output, str(self)) | 478 raise device_errors.CommandFailedError(msg % output, str(self)) |
| (...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 893 # TODO(jbudorick) Evaluate whether we want to return a list of lines after | 904 # TODO(jbudorick) Evaluate whether we want to return a list of lines after |
| 894 # the implementation switch, and if file not found should raise exception. | 905 # the implementation switch, and if file not found should raise exception. |
| 895 if as_root: | 906 if as_root: |
| 896 if not self.old_interface.CanAccessProtectedFileContents(): | 907 if not self.old_interface.CanAccessProtectedFileContents(): |
| 897 raise device_errors.CommandFailedError( | 908 raise device_errors.CommandFailedError( |
| 898 'Cannot read from %s with root privileges.' % device_path) | 909 'Cannot read from %s with root privileges.' % device_path) |
| 899 return self.old_interface.GetProtectedFileContents(device_path) | 910 return self.old_interface.GetProtectedFileContents(device_path) |
| 900 else: | 911 else: |
| 901 return self.old_interface.GetFileContents(device_path) | 912 return self.old_interface.GetFileContents(device_path) |
| 902 | 913 |
| 914 def _WriteFileWithPush(self, device_path, contents): | |
| 915 with tempfile.NamedTemporaryFile() as host_temp: | |
| 916 host_temp.write(contents) | |
| 917 host_temp.flush() | |
| 918 self.adb.Push(host_temp.name, device_path) | |
| 919 | |
| 903 @decorators.WithTimeoutAndRetriesFromInstance() | 920 @decorators.WithTimeoutAndRetriesFromInstance() |
| 904 def WriteFile(self, device_path, contents, as_root=False, force_push=False, | 921 def WriteFile(self, device_path, contents, as_root=False, force_push=False, |
| 905 timeout=None, retries=None): | 922 timeout=None, retries=None): |
| 906 """Writes |contents| to a file on the device. | 923 """Writes |contents| to a file on the device. |
| 907 | 924 |
| 908 Args: | 925 Args: |
| 909 device_path: A string containing the absolute path to the file to write | 926 device_path: A string containing the absolute path to the file to write |
| 910 on the device. | 927 on the device. |
| 911 contents: A string containing the data to write to the device. | 928 contents: A string containing the data to write to the device. |
| 912 as_root: A boolean indicating whether the write should be executed with | 929 as_root: A boolean indicating whether the write should be executed with |
| 913 root privileges (if available). | 930 root privileges (if available). |
| 914 force_push: A boolean indicating whether to force the operation to be | 931 force_push: A boolean indicating whether to force the operation to be |
| 915 performed by pushing a file to the device. The default is, when the | 932 performed by pushing a file to the device. The default is, when the |
| 916 contents are short, to pass the contents using a shell script instead. | 933 contents are short, to pass the contents using a shell script instead. |
| 917 timeout: timeout in seconds | 934 timeout: timeout in seconds |
| 918 retries: number of retries | 935 retries: number of retries |
| 919 | 936 |
| 920 Raises: | 937 Raises: |
| 921 CommandFailedError if the file could not be written on the device. | 938 CommandFailedError if the file could not be written on the device. |
| 922 CommandTimeoutError on timeout. | 939 CommandTimeoutError on timeout. |
| 923 DeviceUnreachableError on missing device. | 940 DeviceUnreachableError on missing device. |
| 924 """ | 941 """ |
| 925 if len(contents) < 512 and not force_push: | 942 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: |
| 943 # If the contents are small, for efficieny we write the contents with | |
| 944 # a shell command rather than pushing a file. | |
| 926 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), | 945 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), |
| 927 cmd_helper.SingleQuote(device_path)) | 946 cmd_helper.SingleQuote(device_path)) |
| 928 self.RunShellCommand(cmd, as_root=as_root, check_return=True) | 947 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| 948 elif as_root and self.NeedsSU(): | |
| 949 # Adb does not allow to "push with su", so we first push to a temp file | |
| 950 # on a safe location, and then copy it to the desired location with su. | |
| 951 with device_temp_file.DeviceTempFile(self.adb) as device_temp: | |
| 952 self._WriteFileWithPush(device_temp.name, contents) | |
| 953 # Here we need 'cp' rather than 'mv' because the temp and | |
| 954 # destination files might be on different file systems (e.g. | |
| 955 # on internal storage and an external sd card). | |
| 956 self.RunShellCommand(['cp', device_temp.name, device_path], | |
| 957 as_root=True, check_return=True) | |
| 929 else: | 958 else: |
| 930 with tempfile.NamedTemporaryFile() as host_temp: | 959 # If root is not needed, we can push directly to the desired location. |
| 931 host_temp.write(contents) | 960 self._WriteFileWithPush(device_path, contents) |
| 932 host_temp.flush() | |
| 933 if as_root and self.NeedsSU(): | |
| 934 with device_temp_file.DeviceTempFile(self.adb) as device_temp: | |
| 935 self.adb.Push(host_temp.name, device_temp.name) | |
| 936 # Here we need 'cp' rather than 'mv' because the temp and | |
| 937 # destination files might be on different file systems (e.g. | |
| 938 # on internal storage and an external sd card) | |
| 939 self.RunShellCommand(['cp', device_temp.name, device_path], | |
| 940 as_root=True, check_return=True) | |
| 941 else: | |
| 942 self.adb.Push(host_temp.name, device_path) | |
| 943 | 961 |
| 944 @decorators.WithTimeoutAndRetriesFromInstance() | 962 @decorators.WithTimeoutAndRetriesFromInstance() |
| 945 def Ls(self, device_path, timeout=None, retries=None): | 963 def Ls(self, device_path, timeout=None, retries=None): |
| 946 """Lists the contents of a directory on the device. | 964 """Lists the contents of a directory on the device. |
| 947 | 965 |
| 948 Args: | 966 Args: |
| 949 device_path: A string containing the path of the directory on the device | 967 device_path: A string containing the path of the directory on the device |
| 950 to list. | 968 to list. |
| 951 timeout: timeout in seconds | 969 timeout: timeout in seconds |
| 952 retries: number of retries | 970 retries: number of retries |
| (...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1269 Returns: | 1287 Returns: |
| 1270 A Parallelizer operating over |devices|. | 1288 A Parallelizer operating over |devices|. |
| 1271 """ | 1289 """ |
| 1272 if not devices: | 1290 if not devices: |
| 1273 devices = adb_wrapper.AdbWrapper.GetDevices() | 1291 devices = adb_wrapper.AdbWrapper.GetDevices() |
| 1274 devices = [d if isinstance(d, cls) else cls(d) for d in devices] | 1292 devices = [d if isinstance(d, cls) else cls(d) for d in devices] |
| 1275 if async: | 1293 if async: |
| 1276 return parallelizer.Parallelizer(devices) | 1294 return parallelizer.Parallelizer(devices) |
| 1277 else: | 1295 else: |
| 1278 return parallelizer.SyncParallelizer(devices) | 1296 return parallelizer.SyncParallelizer(devices) |
| OLD | NEW |