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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 51 """Restarts the adb server. | 51 """Restarts the adb server. |
| 52 | 52 |
| 53 Raises: | 53 Raises: |
| 54 CommandFailedError if we fail to kill or restart the server. | 54 CommandFailedError if we fail to kill or restart the server. |
| 55 """ | 55 """ |
| 56 pylib.android_commands.AndroidCommands().RestartAdbServer() | 56 pylib.android_commands.AndroidCommands().RestartAdbServer() |
| 57 | 57 |
| 58 | 58 |
| 59 class DeviceUtils(object): | 59 class DeviceUtils(object): |
| 60 | 60 |
| 61 _MAX_ADB_COMMAND_LENGTH = 512 | |
| 61 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 62 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
| 62 | 63 |
| 63 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 64 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
| 64 default_retries=_DEFAULT_RETRIES): | 65 default_retries=_DEFAULT_RETRIES): |
| 65 """DeviceUtils constructor. | 66 """DeviceUtils constructor. |
| 66 | 67 |
| 67 Args: | 68 Args: |
| 68 device: Either a device serial, an existing AdbWrapper instance, an | 69 device: Either a device serial, an existing AdbWrapper instance, an |
| 69 an existing AndroidCommands instance, or nothing. | 70 an existing AndroidCommands instance, or nothing. |
| 70 default_timeout: An integer containing the default number of seconds to | 71 default_timeout: An integer containing the default number of seconds to |
| (...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 more lines. | 421 more lines. |
| 421 CommandTimeoutError on timeout. | 422 CommandTimeoutError on timeout. |
| 422 DeviceUnreachableError on missing device. | 423 DeviceUnreachableError on missing device. |
| 423 """ | 424 """ |
| 424 def env_quote(key, value): | 425 def env_quote(key, value): |
| 425 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 426 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
| 426 raise KeyError('Invalid shell variable name %r' % key) | 427 raise KeyError('Invalid shell variable name %r' % key) |
| 427 # using double quotes here to allow interpolation of shell variables | 428 # using double quotes here to allow interpolation of shell variables |
| 428 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 429 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
| 429 | 430 |
| 431 def do_run(cmd): | |
| 432 try: | |
| 433 return self.adb.Shell(cmd) | |
| 434 except device_errors.AdbCommandFailedError as exc: | |
| 435 if check_return: | |
| 436 raise | |
| 437 else: | |
| 438 return exc.output | |
| 439 | |
| 430 if not isinstance(cmd, basestring): | 440 if not isinstance(cmd, basestring): |
| 431 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 441 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
| 432 if env: | 442 if env: |
| 433 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 443 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
| 434 cmd = '%s %s' % (env, cmd) | 444 cmd = '%s %s' % (env, cmd) |
| 435 if cwd: | 445 if cwd: |
| 436 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 446 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
| 437 if as_root and self.NeedsSU(): | 447 if as_root and self.NeedsSU(): |
| 438 # "su -c sh -c" allows using shell features in |cmd| | 448 # "su -c sh -c" allows using shell features in |cmd| |
| 439 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) | 449 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
| 440 if timeout is None: | 450 if timeout is None: |
| 441 timeout = self._default_timeout | 451 timeout = self._default_timeout |
| 442 | 452 |
| 443 try: | 453 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: |
| 444 output = self.adb.Shell(cmd) | 454 output = do_run(cmd) |
| 445 except device_errors.AdbCommandFailedError as e: | 455 else: |
| 446 if check_return: | 456 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
| 447 raise | 457 self._WriteFileWithPush(script.name, cmd) |
| 448 else: | 458 output = do_run('sh %s' % script.name_quoted) |
| 449 output = e.output | |
| 450 | 459 |
| 451 output = output.splitlines() | 460 output = output.splitlines() |
| 452 if single_line: | 461 if single_line: |
| 453 if not output: | 462 if not output: |
| 454 return '' | 463 return '' |
| 455 elif len(output) == 1: | 464 elif len(output) == 1: |
| 456 return output[0] | 465 return output[0] |
| 457 else: | 466 else: |
| 458 msg = 'one line of output was expected, but got: %s' | 467 msg = 'one line of output was expected, but got: %s' |
| 459 raise device_errors.CommandFailedError(msg % output, str(self)) | 468 raise device_errors.CommandFailedError(msg % output, str(self)) |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 880 # TODO(jbudorick) Evaluate whether we want to return a list of lines after | 889 # TODO(jbudorick) Evaluate whether we want to return a list of lines after |
| 881 # the implementation switch, and if file not found should raise exception. | 890 # the implementation switch, and if file not found should raise exception. |
| 882 if as_root: | 891 if as_root: |
| 883 if not self.old_interface.CanAccessProtectedFileContents(): | 892 if not self.old_interface.CanAccessProtectedFileContents(): |
| 884 raise device_errors.CommandFailedError( | 893 raise device_errors.CommandFailedError( |
| 885 'Cannot read from %s with root privileges.' % device_path) | 894 'Cannot read from %s with root privileges.' % device_path) |
| 886 return self.old_interface.GetProtectedFileContents(device_path) | 895 return self.old_interface.GetProtectedFileContents(device_path) |
| 887 else: | 896 else: |
| 888 return self.old_interface.GetFileContents(device_path) | 897 return self.old_interface.GetFileContents(device_path) |
| 889 | 898 |
| 899 def _WriteFileWithPush(self, device_path, contents): | |
| 900 with tempfile.NamedTemporaryFile() as host_temp: | |
| 901 host_temp.write(contents) | |
| 902 host_temp.flush() | |
| 903 self.adb.Push(host_temp.name, device_path) | |
| 904 | |
| 890 @decorators.WithTimeoutAndRetriesFromInstance() | 905 @decorators.WithTimeoutAndRetriesFromInstance() |
| 891 def WriteFile(self, device_path, contents, as_root=False, force_push=False, | 906 def WriteFile(self, device_path, contents, as_root=False, force_push=False, |
| 892 timeout=None, retries=None): | 907 timeout=None, retries=None): |
| 893 """Writes |contents| to a file on the device. | 908 """Writes |contents| to a file on the device. |
| 894 | 909 |
| 895 Args: | 910 Args: |
| 896 device_path: A string containing the absolute path to the file to write | 911 device_path: A string containing the absolute path to the file to write |
| 897 on the device. | 912 on the device. |
| 898 contents: A string containing the data to write to the device. | 913 contents: A string containing the data to write to the device. |
| 899 as_root: A boolean indicating whether the write should be executed with | 914 as_root: A boolean indicating whether the write should be executed with |
| 900 root privileges (if available). | 915 root privileges (if available). |
| 901 force_push: A boolean indicating whether to force the operation to be | 916 force_push: A boolean indicating whether to force the operation to be |
| 902 performed by pushing a file to the device. The default is, when the | 917 performed by pushing a file to the device. The default is, when the |
| 903 contents are short, to pass the contents using a shell script instead. | 918 contents are short, to pass the contents using a shell script instead. |
| 904 timeout: timeout in seconds | 919 timeout: timeout in seconds |
| 905 retries: number of retries | 920 retries: number of retries |
| 906 | 921 |
| 907 Raises: | 922 Raises: |
| 908 CommandFailedError if the file could not be written on the device. | 923 CommandFailedError if the file could not be written on the device. |
| 909 CommandTimeoutError on timeout. | 924 CommandTimeoutError on timeout. |
| 910 DeviceUnreachableError on missing device. | 925 DeviceUnreachableError on missing device. |
| 911 """ | 926 """ |
| 912 if len(contents) < 512 and not force_push: | 927 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: |
|
jbudorick
2014/12/04 15:34:01
nit: Can we comment the reason for the 3-way split
| |
| 913 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), | 928 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), |
| 914 cmd_helper.SingleQuote(device_path)) | 929 cmd_helper.SingleQuote(device_path)) |
| 915 self.RunShellCommand(cmd, as_root=as_root, check_return=True) | 930 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| 931 elif as_root and self.NeedsSU(): | |
| 932 with device_temp_file.DeviceTempFile(self.adb) as device_temp: | |
| 933 self._WriteFileWithPush(device_temp.name, contents) | |
| 934 # Here we need 'cp' rather than 'mv' because the temp and | |
| 935 # destination files might be on different file systems (e.g. | |
| 936 # on internal storage and an external sd card) | |
| 937 self.RunShellCommand(['cp', device_temp.name, device_path], | |
| 938 as_root=True, check_return=True) | |
| 916 else: | 939 else: |
| 917 with tempfile.NamedTemporaryFile() as host_temp: | 940 self._WriteFileWithPush(device_path, contents) |
| 918 host_temp.write(contents) | |
| 919 host_temp.flush() | |
| 920 if as_root and self.NeedsSU(): | |
| 921 with device_temp_file.DeviceTempFile(self) as device_temp: | |
| 922 self.adb.Push(host_temp.name, device_temp.name) | |
| 923 # Here we need 'cp' rather than 'mv' because the temp and | |
| 924 # destination files might be on different file systems (e.g. | |
| 925 # on internal storage and an external sd card) | |
| 926 self.RunShellCommand(['cp', device_temp.name, device_path], | |
| 927 as_root=True, check_return=True) | |
| 928 else: | |
| 929 self.adb.Push(host_temp.name, device_path) | |
| 930 | 941 |
| 931 @decorators.WithTimeoutAndRetriesFromInstance() | 942 @decorators.WithTimeoutAndRetriesFromInstance() |
| 932 def Ls(self, device_path, timeout=None, retries=None): | 943 def Ls(self, device_path, timeout=None, retries=None): |
| 933 """Lists the contents of a directory on the device. | 944 """Lists the contents of a directory on the device. |
| 934 | 945 |
| 935 Args: | 946 Args: |
| 936 device_path: A string containing the path of the directory on the device | 947 device_path: A string containing the path of the directory on the device |
| 937 to list. | 948 to list. |
| 938 timeout: timeout in seconds | 949 timeout: timeout in seconds |
| 939 retries: number of retries | 950 retries: number of retries |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1167 Returns: | 1178 Returns: |
| 1168 A Parallelizer operating over |devices|. | 1179 A Parallelizer operating over |devices|. |
| 1169 """ | 1180 """ |
| 1170 if not devices or len(devices) == 0: | 1181 if not devices or len(devices) == 0: |
| 1171 devices = pylib.android_commands.GetAttachedDevices() | 1182 devices = pylib.android_commands.GetAttachedDevices() |
| 1172 parallelizer_type = (parallelizer.Parallelizer if async | 1183 parallelizer_type = (parallelizer.Parallelizer if async |
| 1173 else parallelizer.SyncParallelizer) | 1184 else parallelizer.SyncParallelizer) |
| 1174 return parallelizer_type([ | 1185 return parallelizer_type([ |
| 1175 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 1186 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
| 1176 for d in devices]) | 1187 for d in devices]) |
| OLD | NEW |