| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 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 """This module wraps Android's adb tool. | 5 """This module wraps Android's adb tool. |
| 6 | 6 |
| 7 This is a thin wrapper around the adb interface. Any additional complexity | 7 This is a thin wrapper around the adb interface. Any additional complexity |
| 8 should be delegated to a higher level (ex. DeviceUtils). | 8 should be delegated to a higher level (ex. DeviceUtils). |
| 9 """ | 9 """ |
| 10 | 10 |
| 11 import collections | 11 import collections |
| 12 import errno | 12 import errno |
| 13 import logging | 13 import logging |
| 14 import os | 14 import os |
| 15 import re | 15 import re |
| 16 import subprocess |
| 16 | 17 |
| 17 from devil import devil_env | 18 from devil import devil_env |
| 18 from devil.android import decorators | 19 from devil.android import decorators |
| 19 from devil.android import device_errors | 20 from devil.android import device_errors |
| 20 from devil.utils import cmd_helper | 21 from devil.utils import cmd_helper |
| 21 from devil.utils import lazy | 22 from devil.utils import lazy |
| 22 from devil.utils import timeout_retry | 23 from devil.utils import timeout_retry |
| 23 | 24 |
| 24 with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH): | 25 with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH): |
| 25 import dependency_manager # pylint: disable=import-error | 26 import dependency_manager # pylint: disable=import-error |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 | 66 |
| 66 | 67 |
| 67 def _ShouldRetryAdbCmd(exc): | 68 def _ShouldRetryAdbCmd(exc): |
| 68 return not isinstance(exc, device_errors.NoAdbError) | 69 return not isinstance(exc, device_errors.NoAdbError) |
| 69 | 70 |
| 70 | 71 |
| 71 DeviceStat = collections.namedtuple('DeviceStat', | 72 DeviceStat = collections.namedtuple('DeviceStat', |
| 72 ['st_mode', 'st_size', 'st_time']) | 73 ['st_mode', 'st_size', 'st_time']) |
| 73 | 74 |
| 74 | 75 |
| 76 def _IsExtraneousLine(line, send_cmd): |
| 77 """Determine if a line read from stdout in persistent shell is extraneous. |
| 78 |
| 79 The results output to stdout by the persistent shell process |
| 80 (in PersistentShell below) often include "extraneous" lines that are |
| 81 not part of the output of the shell command. These "extraneous" lines |
| 82 do not always appear and are of two forms: shell prompt lines and lines |
| 83 that just duplicate what the input command was. This function |
| 84 detects these extraneous lines. Since all these lines have the |
| 85 original command in them, that is what it detects ror. |
| 86 |
| 87 Args: |
| 88 line: Output line to check. |
| 89 send_cmd: Command that was sent to adb persistent shell. |
| 90 """ |
| 91 return send_cmd.rstrip() in line |
| 92 |
| 93 |
| 75 class AdbWrapper(object): | 94 class AdbWrapper(object): |
| 76 """A wrapper around a local Android Debug Bridge executable.""" | 95 """A wrapper around a local Android Debug Bridge executable.""" |
| 77 | 96 |
| 78 _adb_path = lazy.WeakConstant(_FindAdb) | 97 _adb_path = lazy.WeakConstant(_FindAdb) |
| 79 | 98 |
| 80 def __init__(self, device_serial): | 99 def __init__(self, device_serial): |
| 81 """Initializes the AdbWrapper. | 100 """Initializes the AdbWrapper. |
| 82 | 101 |
| 83 Args: | 102 Args: |
| 84 device_serial: The device serial number as a string. | 103 device_serial: The device serial number as a string. |
| 85 """ | 104 """ |
| 86 if not device_serial: | 105 if not device_serial: |
| 87 raise ValueError('A device serial must be specified') | 106 raise ValueError('A device serial must be specified') |
| 88 self._device_serial = str(device_serial) | 107 self._device_serial = str(device_serial) |
| 89 | 108 |
| 109 class PersistentShell(object): |
| 110 '''Class to use persistent shell for ADB. |
| 111 |
| 112 This class allows a persistent ADB shell to be created, where multiple |
| 113 commands can be passed into it. This avoids the overhead of starting |
| 114 up a new ADB shell for each command. |
| 115 |
| 116 Example of use: |
| 117 with pshell as PersistentShell('123456789'): |
| 118 pshell.RunCommand('which ls') |
| 119 pshell.RunCommandAndClose('echo TEST') |
| 120 ''' |
| 121 def __init__(self, serial): |
| 122 """Initialization function: |
| 123 |
| 124 Args: |
| 125 serial: Serial number of device. |
| 126 """ |
| 127 self._cmd = [AdbWrapper.GetAdbPath(), '-s', serial, 'shell'] |
| 128 self._process = None |
| 129 |
| 130 def __enter__(self): |
| 131 self.Start() |
| 132 self.WaitForReady() |
| 133 return self |
| 134 |
| 135 def __exit__(self, exc_type, exc_value, tb): |
| 136 self.Stop() |
| 137 |
| 138 def Start(self): |
| 139 """Start the shell.""" |
| 140 if self._process is not None: |
| 141 raise RuntimeError('Persistent shell already running.') |
| 142 self._process = subprocess.Popen(self._cmd, |
| 143 stdin=subprocess.PIPE, |
| 144 stdout=subprocess.PIPE, |
| 145 shell=False) |
| 146 |
| 147 def WaitForReady(self): |
| 148 """Wait for the shell to be ready after starting. |
| 149 |
| 150 Sends an echo command, then waits until it gets a response. |
| 151 """ |
| 152 self._process.stdin.write('echo\n') |
| 153 output_line = self._process.stdout.readline() |
| 154 while output_line.rstrip() != '': |
| 155 output_line = self._process.stdout.readline() |
| 156 |
| 157 def RunCommand(self, command, close=False): |
| 158 """Runs an ADB command and returns the output. |
| 159 |
| 160 Note that there can be approximately 40 ms of additional latency |
| 161 between sending the command and receiving the results if close=False |
| 162 due to the use of Nagle's algorithm in the TCP socket between the |
| 163 adb server and client. To avoid this extra latency, set close=True. |
| 164 |
| 165 Args: |
| 166 command: Command to send. |
| 167 Returns: |
| 168 The command output, given as a list of lines, and the exit code |
| 169 """ |
| 170 |
| 171 if close: |
| 172 def run_cmd(cmd): |
| 173 send_cmd = '( %s ); echo $?; exit;\n' % cmd.rstrip() |
| 174 (output, _) = self._process.communicate(send_cmd) |
| 175 self._process = None |
| 176 for x in output.splitlines(): |
| 177 yield x |
| 178 |
| 179 else: |
| 180 def run_cmd(cmd): |
| 181 send_cmd = '( %s ); echo DONE:$?;\n' % cmd.rstrip() |
| 182 self._process.stdin.write(send_cmd) |
| 183 while True: |
| 184 output_line = self._process.stdout.readline().rstrip() |
| 185 if output_line[:5] == 'DONE:': |
| 186 yield output_line[5:] |
| 187 break |
| 188 yield output_line |
| 189 |
| 190 result = [line for line in run_cmd(command) |
| 191 if not _IsExtraneousLine(line, command)] |
| 192 |
| 193 return (result[:-1], int(result[-1])) |
| 194 |
| 195 def Stop(self): |
| 196 """Stops the ADB process if it is still running.""" |
| 197 if self._process is not None: |
| 198 self._process.stdin.write('exit\n') |
| 199 self._process = None |
| 200 |
| 90 @classmethod | 201 @classmethod |
| 91 def GetAdbPath(cls): | 202 def GetAdbPath(cls): |
| 92 return cls._adb_path.read() | 203 return cls._adb_path.read() |
| 93 | 204 |
| 94 @classmethod | 205 @classmethod |
| 95 def _BuildAdbCmd(cls, args, device_serial, cpu_affinity=None): | 206 def _BuildAdbCmd(cls, args, device_serial, cpu_affinity=None): |
| 96 if cpu_affinity is not None: | 207 if cpu_affinity is not None: |
| 97 cmd = ['taskset', '-c', str(cpu_affinity)] | 208 cmd = ['taskset', '-c', str(cpu_affinity)] |
| 98 else: | 209 else: |
| 99 cmd = [] | 210 cmd = [] |
| (...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 695 @property | 806 @property |
| 696 def is_emulator(self): | 807 def is_emulator(self): |
| 697 return _EMULATOR_RE.match(self._device_serial) | 808 return _EMULATOR_RE.match(self._device_serial) |
| 698 | 809 |
| 699 @property | 810 @property |
| 700 def is_ready(self): | 811 def is_ready(self): |
| 701 try: | 812 try: |
| 702 return self.GetState() == _READY_STATE | 813 return self.GetState() == _READY_STATE |
| 703 except device_errors.CommandFailedError: | 814 except device_errors.CommandFailedError: |
| 704 return False | 815 return False |
| OLD | NEW |