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 |