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 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 | 93 |
94 def _JoinLines(lines): | 94 def _JoinLines(lines): |
95 # makes sure that the last line is also terminated, and is more memory | 95 # makes sure that the last line is also terminated, and is more memory |
96 # efficient than first appending an end-line to each line and then joining | 96 # efficient than first appending an end-line to each line and then joining |
97 # all of them together. | 97 # all of them together. |
98 return ''.join(s for line in lines for s in (line, '\n')) | 98 return ''.join(s for line in lines for s in (line, '\n')) |
99 | 99 |
100 | 100 |
101 class DeviceUtils(object): | 101 class DeviceUtils(object): |
102 | 102 |
| 103 _MAX_ADB_COMMAND_LENGTH = 512 |
103 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 104 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
104 | 105 |
105 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 106 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
106 default_retries=_DEFAULT_RETRIES): | 107 default_retries=_DEFAULT_RETRIES): |
107 """DeviceUtils constructor. | 108 """DeviceUtils constructor. |
108 | 109 |
109 Args: | 110 Args: |
110 device: Either a device serial, an existing AdbWrapper instance, or an | 111 device: Either a device serial, an existing AdbWrapper instance, or an |
111 an existing AndroidCommands instance. | 112 an existing AndroidCommands instance. |
112 default_timeout: An integer containing the default number of seconds to | 113 default_timeout: An integer containing the default number of seconds to |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 more lines. | 455 more lines. |
455 CommandTimeoutError on timeout. | 456 CommandTimeoutError on timeout. |
456 DeviceUnreachableError on missing device. | 457 DeviceUnreachableError on missing device. |
457 """ | 458 """ |
458 def env_quote(key, value): | 459 def env_quote(key, value): |
459 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 460 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
460 raise KeyError('Invalid shell variable name %r' % key) | 461 raise KeyError('Invalid shell variable name %r' % key) |
461 # using double quotes here to allow interpolation of shell variables | 462 # using double quotes here to allow interpolation of shell variables |
462 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 463 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
463 | 464 |
| 465 def do_run(cmd): |
| 466 try: |
| 467 return self.adb.Shell(cmd) |
| 468 except device_errors.AdbCommandFailedError as exc: |
| 469 if check_return: |
| 470 raise |
| 471 else: |
| 472 return exc.output |
| 473 |
464 if not isinstance(cmd, basestring): | 474 if not isinstance(cmd, basestring): |
465 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 475 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
466 if env: | 476 if env: |
467 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 477 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
468 cmd = '%s %s' % (env, cmd) | 478 cmd = '%s %s' % (env, cmd) |
469 if cwd: | 479 if cwd: |
470 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 480 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
471 if as_root and self.NeedsSU(): | 481 if as_root and self.NeedsSU(): |
472 # "su -c sh -c" allows using shell features in |cmd| | 482 # "su -c sh -c" allows using shell features in |cmd| |
473 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) | 483 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
474 if timeout is None: | 484 if timeout is None: |
475 timeout = self._default_timeout | 485 timeout = self._default_timeout |
476 | 486 |
477 try: | 487 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: |
478 output = self.adb.Shell(cmd) | 488 output = do_run(cmd) |
479 except device_errors.AdbCommandFailedError as e: | 489 else: |
480 if check_return: | 490 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
481 raise | 491 self._WriteFileWithPush(script.name, cmd) |
482 else: | 492 logging.info('Large shell command will be run from file: %s ...', |
483 output = e.output | 493 cmd[:100]) |
| 494 output = do_run('sh %s' % script.name_quoted) |
484 | 495 |
485 output = output.splitlines() | 496 output = output.splitlines() |
486 if single_line: | 497 if single_line: |
487 if not output: | 498 if not output: |
488 return '' | 499 return '' |
489 elif len(output) == 1: | 500 elif len(output) == 1: |
490 return output[0] | 501 return output[0] |
491 else: | 502 else: |
492 msg = 'one line of output was expected, but got: %s' | 503 msg = 'one line of output was expected, but got: %s' |
493 raise device_errors.CommandFailedError(msg % output, str(self)) | 504 raise device_errors.CommandFailedError(msg % output, str(self)) |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
569 extras=None, timeout=None, retries=None): | 580 extras=None, timeout=None, retries=None): |
570 if extras is None: | 581 if extras is None: |
571 extras = {} | 582 extras = {} |
572 | 583 |
573 cmd = ['am', 'instrument'] | 584 cmd = ['am', 'instrument'] |
574 if finish: | 585 if finish: |
575 cmd.append('-w') | 586 cmd.append('-w') |
576 if raw: | 587 if raw: |
577 cmd.append('-r') | 588 cmd.append('-r') |
578 for k, v in extras.iteritems(): | 589 for k, v in extras.iteritems(): |
579 cmd.extend(['-e', k, v]) | 590 cmd.extend(['-e', str(k), str(v)]) |
580 cmd.append(component) | 591 cmd.append(component) |
581 return self.RunShellCommand(cmd, check_return=True) | 592 return self.RunShellCommand(cmd, check_return=True) |
582 | 593 |
583 @decorators.WithTimeoutAndRetriesFromInstance() | 594 @decorators.WithTimeoutAndRetriesFromInstance() |
584 def BroadcastIntent(self, intent_obj, timeout=None, retries=None): | 595 def BroadcastIntent(self, intent_obj, timeout=None, retries=None): |
585 """Send a broadcast intent. | 596 """Send a broadcast intent. |
586 | 597 |
587 Args: | 598 Args: |
588 intent: An Intent to broadcast. | 599 intent: An Intent to broadcast. |
589 timeout: timeout in seconds | 600 timeout: timeout in seconds |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
908 all lines will be terminated. | 919 all lines will be terminated. |
909 | 920 |
910 Raises: | 921 Raises: |
911 AdbCommandFailedError if the file can't be read. | 922 AdbCommandFailedError if the file can't be read. |
912 CommandTimeoutError on timeout. | 923 CommandTimeoutError on timeout. |
913 DeviceUnreachableError on missing device. | 924 DeviceUnreachableError on missing device. |
914 """ | 925 """ |
915 return _JoinLines(self.RunShellCommand( | 926 return _JoinLines(self.RunShellCommand( |
916 ['cat', device_path], as_root=as_root, check_return=True)) | 927 ['cat', device_path], as_root=as_root, check_return=True)) |
917 | 928 |
| 929 def _WriteFileWithPush(self, device_path, contents): |
| 930 with tempfile.NamedTemporaryFile() as host_temp: |
| 931 host_temp.write(contents) |
| 932 host_temp.flush() |
| 933 self.adb.Push(host_temp.name, device_path) |
| 934 |
918 @decorators.WithTimeoutAndRetriesFromInstance() | 935 @decorators.WithTimeoutAndRetriesFromInstance() |
919 def WriteFile(self, device_path, contents, as_root=False, force_push=False, | 936 def WriteFile(self, device_path, contents, as_root=False, force_push=False, |
920 timeout=None, retries=None): | 937 timeout=None, retries=None): |
921 """Writes |contents| to a file on the device. | 938 """Writes |contents| to a file on the device. |
922 | 939 |
923 Args: | 940 Args: |
924 device_path: A string containing the absolute path to the file to write | 941 device_path: A string containing the absolute path to the file to write |
925 on the device. | 942 on the device. |
926 contents: A string containing the data to write to the device. | 943 contents: A string containing the data to write to the device. |
927 as_root: A boolean indicating whether the write should be executed with | 944 as_root: A boolean indicating whether the write should be executed with |
928 root privileges (if available). | 945 root privileges (if available). |
929 force_push: A boolean indicating whether to force the operation to be | 946 force_push: A boolean indicating whether to force the operation to be |
930 performed by pushing a file to the device. The default is, when the | 947 performed by pushing a file to the device. The default is, when the |
931 contents are short, to pass the contents using a shell script instead. | 948 contents are short, to pass the contents using a shell script instead. |
932 timeout: timeout in seconds | 949 timeout: timeout in seconds |
933 retries: number of retries | 950 retries: number of retries |
934 | 951 |
935 Raises: | 952 Raises: |
936 CommandFailedError if the file could not be written on the device. | 953 CommandFailedError if the file could not be written on the device. |
937 CommandTimeoutError on timeout. | 954 CommandTimeoutError on timeout. |
938 DeviceUnreachableError on missing device. | 955 DeviceUnreachableError on missing device. |
939 """ | 956 """ |
940 if len(contents) < 512 and not force_push: | 957 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: |
| 958 # If the contents are small, for efficieny we write the contents with |
| 959 # a shell command rather than pushing a file. |
941 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), | 960 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), |
942 cmd_helper.SingleQuote(device_path)) | 961 cmd_helper.SingleQuote(device_path)) |
943 self.RunShellCommand(cmd, as_root=as_root, check_return=True) | 962 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| 963 elif as_root and self.NeedsSU(): |
| 964 # Adb does not allow to "push with su", so we first push to a temp file |
| 965 # on a safe location, and then copy it to the desired location with su. |
| 966 with device_temp_file.DeviceTempFile(self.adb) as device_temp: |
| 967 self._WriteFileWithPush(device_temp.name, contents) |
| 968 # Here we need 'cp' rather than 'mv' because the temp and |
| 969 # destination files might be on different file systems (e.g. |
| 970 # on internal storage and an external sd card). |
| 971 self.RunShellCommand(['cp', device_temp.name, device_path], |
| 972 as_root=True, check_return=True) |
944 else: | 973 else: |
945 with tempfile.NamedTemporaryFile() as host_temp: | 974 # If root is not needed, we can push directly to the desired location. |
946 host_temp.write(contents) | 975 self._WriteFileWithPush(device_path, contents) |
947 host_temp.flush() | |
948 if as_root and self.NeedsSU(): | |
949 with device_temp_file.DeviceTempFile(self.adb) as device_temp: | |
950 self.adb.Push(host_temp.name, device_temp.name) | |
951 # Here we need 'cp' rather than 'mv' because the temp and | |
952 # destination files might be on different file systems (e.g. | |
953 # on internal storage and an external sd card) | |
954 self.RunShellCommand(['cp', device_temp.name, device_path], | |
955 as_root=True, check_return=True) | |
956 else: | |
957 self.adb.Push(host_temp.name, device_path) | |
958 | 976 |
959 @decorators.WithTimeoutAndRetriesFromInstance() | 977 @decorators.WithTimeoutAndRetriesFromInstance() |
960 def Ls(self, device_path, timeout=None, retries=None): | 978 def Ls(self, device_path, timeout=None, retries=None): |
961 """Lists the contents of a directory on the device. | 979 """Lists the contents of a directory on the device. |
962 | 980 |
963 Args: | 981 Args: |
964 device_path: A string containing the path of the directory on the device | 982 device_path: A string containing the path of the directory on the device |
965 to list. | 983 to list. |
966 timeout: timeout in seconds | 984 timeout: timeout in seconds |
967 retries: number of retries | 985 retries: number of retries |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1291 Returns: | 1309 Returns: |
1292 A Parallelizer operating over |devices|. | 1310 A Parallelizer operating over |devices|. |
1293 """ | 1311 """ |
1294 if not devices: | 1312 if not devices: |
1295 devices = adb_wrapper.AdbWrapper.GetDevices() | 1313 devices = adb_wrapper.AdbWrapper.GetDevices() |
1296 devices = [d if isinstance(d, cls) else cls(d) for d in devices] | 1314 devices = [d if isinstance(d, cls) else cls(d) for d in devices] |
1297 if async: | 1315 if async: |
1298 return parallelizer.Parallelizer(devices) | 1316 return parallelizer.Parallelizer(devices) |
1299 else: | 1317 else: |
1300 return parallelizer.SyncParallelizer(devices) | 1318 return parallelizer.SyncParallelizer(devices) |
OLD | NEW |