Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: devil/devil/android/sdk/adb_wrapper.py

Issue 1748173002: New API for persistent ADB shell (Closed) Base URL: git@github.com:catapult-project/catapult@master
Patch Set: fix nits Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | devil/devil/android/sdk/adb_wrapper_devicetest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « no previous file | devil/devil/android/sdk/adb_wrapper_devicetest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698