We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Propagate stderr from adb commands
The current mobly adb proxy does not propagate stderr if ret code is zero.
We thought this was ok since Android has fixed return code issues in M.

But turns out many China manufacturers did not fix this in China devices.
In order to better support China devices and potentially other devices of the same ret code problem, we need to surface stderr.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mobly/controllers/android_device_lib/adb.py`
Content:
```
1 # Copyright 2016 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from builtins import str
16 from past.builtins import basestring
17 
18 import logging
19 import pipes
20 import psutil
21 import subprocess
22 import threading
23 
24 # Command to use for running ADB commands.
25 ADB = 'adb'
26 
27 # adb gets confused if we try to manage bound ports in parallel, so anything to
28 # do with port forwarding must happen under this lock.
29 ADB_PORT_LOCK = threading.Lock()
30 
31 # Qualified class name of the default instrumentation test runner.
32 DEFAULT_INSTRUMENTATION_RUNNER = 'com.android.common.support.test.runner.AndroidJUnitRunner'
33 
34 
35 class Error(Exception):
36     """Base error type for adb proxy module."""
37 
38 
39 class AdbError(Error):
40     """Raised when an adb command encounters an error.
41 
42     Args:
43         cmd: list of strings, the adb command executed.
44         stdout: byte string, the raw stdout of the command.
45         stderr: byte string, the raw stderr of the command.
46         ret_code: int, the return code of the command.
47     """
48 
49     def __init__(self, cmd, stdout, stderr, ret_code):
50         self.cmd = cmd
51         self.stdout = stdout
52         self.stderr = stderr
53         self.ret_code = ret_code
54 
55     def __str__(self):
56         return ('Error executing adb cmd "%s". ret: %d, stdout: %s, stderr: %s'
57                 ) % (cli_cmd_to_string(self.cmd), self.ret_code, self.stdout,
58                      self.stderr)
59 
60 
61 class AdbTimeoutError(Error):
62     """Raised when an command did not complete within expected time.
63 
64     Args:
65         cmd: list of strings, the adb command that timed out
66         timeout: float, the number of seconds passed before timing out.
67     """
68 
69     def __init__(self, cmd, timeout):
70         self.cmd = cmd
71         self.timeout = timeout
72 
73     def __str__(self):
74         return 'Timed out executing command "%s" after %ss.' % (
75             cli_cmd_to_string(self.cmd), self.timeout)
76 
77 
78 def list_occupied_adb_ports():
79     """Lists all the host ports occupied by adb forward.
80 
81     This is useful because adb will silently override the binding if an attempt
82     to bind to a port already used by adb was made, instead of throwing binding
83     error. So one should always check what ports adb is using before trying to
84     bind to a port with adb.
85 
86     Returns:
87         A list of integers representing occupied host ports.
88     """
89     out = AdbProxy().forward('--list')
90     clean_lines = str(out, 'utf-8').strip().split('\n')
91     used_ports = []
92     for line in clean_lines:
93         tokens = line.split(' tcp:')
94         if len(tokens) != 3:
95             continue
96         used_ports.append(int(tokens[1]))
97     return used_ports
98 
99 
100 def cli_cmd_to_string(args):
101     """Converts a cmd arg list to string.
102 
103     Args:
104         args: list of strings, the arguments of a command.
105 
106     Returns:
107         String representation of the command.
108     """
109     if isinstance(args, basestring):
110         # Return directly if it's already a string.
111         return args
112     return ' '.join([pipes.quote(arg) for arg in args])
113 
114 
115 class AdbProxy(object):
116     """Proxy class for ADB.
117 
118     For syntactic reasons, the '-' in adb commands need to be replaced with
119     '_'. Can directly execute adb commands on an object:
120     >> adb = AdbProxy(<serial>)
121     >> adb.start_server()
122     >> adb.devices() # will return the console output of "adb devices".
123 
124     By default, command args are expected to be an iterable which is passed
125     directly to subprocess.Popen():
126     >> adb.shell(['echo', 'a', 'b'])
127 
128     This way of launching commands is recommended by the subprocess
129     documentation to avoid shell injection vulnerabilities and avoid having to
130     deal with multiple layers of shell quoting and different shell environments
131     between different OSes.
132 
133     If you really want to run the command through the system shell, this is
134     possible by supplying shell=True, but try to avoid this if possible:
135     >> adb.shell('cat /foo > /tmp/file', shell=True)
136     """
137 
138     def __init__(self, serial=''):
139         self.serial = serial
140 
141     def _exec_cmd(self, args, shell, timeout):
142         """Executes adb commands.
143 
144         Args:
145             args: string or list of strings, program arguments.
146                 See subprocess.Popen() documentation.
147             shell: bool, True to run this command through the system shell,
148                 False to invoke it directly. See subprocess.Popen() docs.
149             timeout: float, the number of seconds to wait before timing out.
150                 If not specified, no timeout takes effect.
151 
152         Returns:
153             The output of the adb command run if exit code is 0.
154 
155         Raises:
156             AdbError: The adb command exit code is not 0.
157             AdbTimeoutError: The adb command timed out.
158         """
159         proc = subprocess.Popen(
160             args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
161         process = psutil.Process(proc.pid)
162         if timeout and timeout <= 0:
163             raise Error('Timeout is not a positive value: %s' % timeout)
164         if timeout and timeout > 0:
165             try:
166                 process.wait(timeout=timeout)
167             except psutil.TimeoutExpired:
168                 process.terminate()
169                 raise AdbTimeoutError(cmd=args, timeout=timeout)
170 
171         (out, err) = proc.communicate()
172         ret = proc.returncode
173         logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',
174                       cli_cmd_to_string(args), out, err, ret)
175         if ret == 0:
176             return out
177         else:
178             raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)
179 
180     def _exec_adb_cmd(self, name, args, shell, timeout):
181         if shell:
182             # Add quotes around "adb" in case the ADB path contains spaces. This
183             # is pretty common on Windows (e.g. Program Files).
184             if self.serial:
185                 adb_cmd = '"%s" -s "%s" %s %s' % (ADB, self.serial, name, args)
186             else:
187                 adb_cmd = '"%s" %s %s' % (ADB, name, args)
188         else:
189             adb_cmd = [ADB]
190             if self.serial:
191                 adb_cmd.extend(['-s', self.serial])
192             adb_cmd.append(name)
193             if args:
194                 if isinstance(args, basestring):
195                     adb_cmd.append(args)
196                 else:
197                     adb_cmd.extend(args)
198         return self._exec_cmd(adb_cmd, shell=shell, timeout=timeout)
199 
200     def getprop(self, prop_name):
201         """Get a property of the device.
202 
203         This is a convenience wrapper for "adb shell getprop xxx".
204 
205         Args:
206             prop_name: A string that is the name of the property to get.
207 
208         Returns:
209             A string that is the value of the property, or None if the property
210             doesn't exist.
211         """
212         return self.shell('getprop %s' % prop_name).decode('utf-8').strip()
213 
214     def has_shell_command(self, command):
215         """Checks to see if a given check command exists on the device.
216 
217         Args:
218             command: A string that is the name of the command to check.
219 
220         Returns:
221             A boolean that is True if the command exists and False otherwise.
222         """
223         try:
224             output = self.shell(['command', '-v',
225                                  command]).decode('utf-8').strip()
226             return command in output
227         except AdbError:
228             # If the command doesn't exist, then 'command -v' can return
229             # an exit code > 1.
230             return False
231 
232     def forward(self, args=None, shell=False):
233         with ADB_PORT_LOCK:
234             return self._exec_adb_cmd('forward', args, shell, timeout=None)
235 
236     def instrument(self, package, options=None, runner=None):
237         """Runs an instrumentation command on the device.
238 
239         This is a convenience wrapper to avoid parameter formatting.
240 
241         Example:
242             device.instrument(
243                 'com.my.package.test',
244                 options = {
245                     'class': 'com.my.package.test.TestSuite',
246                 },
247             )
248 
249         Args:
250             package: string, the package of the instrumentation tests.
251             options: dict, the instrumentation options including the test
252                 class.
253             runner: string, the test runner name, which defaults to
254                 DEFAULT_INSTRUMENTATION_RUNNER.
255 
256         Returns:
257             The output of instrumentation command.
258         """
259         if runner is None:
260             runner = DEFAULT_INSTRUMENTATION_RUNNER
261         if options is None:
262             options = {}
263 
264         options_list = []
265         for option_key, option_value in options.items():
266             options_list.append('-e %s %s' % (option_key, option_value))
267         options_string = ' '.join(options_list)
268 
269         instrumentation_command = 'am instrument -r -w %s %s/%s' % (
270             options_string, package, runner)
271         logging.info('AndroidDevice|%s: Executing adb shell %s', self.serial,
272                      instrumentation_command)
273         return self.shell(instrumentation_command)
274 
275     def __getattr__(self, name):
276         def adb_call(args=None, shell=False, timeout=None):
277             """Wrapper for an ADB command.
278 
279             Args:
280                 args: string or list of strings, arguments to the adb command.
281                     See subprocess.Proc() documentation.
282                 shell: bool, True to run this command through the system shell,
283                     False to invoke it directly. See subprocess.Proc() docs.
284                 timeout: float, the number of seconds to wait before timing out.
285                     If not specified, no timeout takes effect.
286 
287             Returns:
288                 The output of the adb command run if exit code is 0.
289             """
290             args = args or ''
291             clean_name = name.replace('_', '-')
292             return self._exec_adb_cmd(
293                 clean_name, args, shell=shell, timeout=timeout)
294 
295         return adb_call
296
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py
--- a/mobly/controllers/android_device_lib/adb.py
+++ b/mobly/controllers/android_device_lib/adb.py
@@ -138,7 +138,7 @@
     def __init__(self, serial=''):
         self.serial = serial
 
-    def _exec_cmd(self, args, shell, timeout):
+    def _exec_cmd(self, args, shell, timeout, stderr):
         """Executes adb commands.
 
         Args:
@@ -148,6 +148,8 @@
                 False to invoke it directly. See subprocess.Popen() docs.
             timeout: float, the number of seconds to wait before timing out.
                 If not specified, no timeout takes effect.
+            stderr: a Byte stream, like io.BytesIO, stderr of the command will
+                be written to this object if provided.
 
         Returns:
             The output of the adb command run if exit code is 0.
@@ -169,6 +171,8 @@
                 raise AdbTimeoutError(cmd=args, timeout=timeout)
 
         (out, err) = proc.communicate()
+        if stderr:
+            stderr.write(err)
         ret = proc.returncode
         logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',
                       cli_cmd_to_string(args), out, err, ret)
@@ -177,7 +181,7 @@
         else:
             raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)
 
-    def _exec_adb_cmd(self, name, args, shell, timeout):
+    def _exec_adb_cmd(self, name, args, shell, timeout, stderr):
         if shell:
             # Add quotes around "adb" in case the ADB path contains spaces. This
             # is pretty common on Windows (e.g. Program Files).
@@ -195,7 +199,9 @@
                     adb_cmd.append(args)
                 else:
                     adb_cmd.extend(args)
-        return self._exec_cmd(adb_cmd, shell=shell, timeout=timeout)
+        out = self._exec_cmd(
+            adb_cmd, shell=shell, timeout=timeout, stderr=stderr)
+        return out
 
     def getprop(self, prop_name):
         """Get a property of the device.
@@ -273,7 +279,7 @@
         return self.shell(instrumentation_command)
 
     def __getattr__(self, name):
-        def adb_call(args=None, shell=False, timeout=None):
+        def adb_call(args=None, shell=False, timeout=None, stderr=None):
             """Wrapper for an ADB command.
 
             Args:
@@ -283,6 +289,8 @@
                     False to invoke it directly. See subprocess.Proc() docs.
                 timeout: float, the number of seconds to wait before timing out.
                     If not specified, no timeout takes effect.
+                stderr: a Byte stream, like io.BytesIO, stderr of the command
+                    will be written to this object if provided.
 
             Returns:
                 The output of the adb command run if exit code is 0.
@@ -290,6 +298,6 @@
             args = args or ''
             clean_name = name.replace('_', '-')
             return self._exec_adb_cmd(
-                clean_name, args, shell=shell, timeout=timeout)
+                clean_name, args, shell=shell, timeout=timeout, stderr=stderr)
 
         return adb_call

{"golden_diff": "diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py\n--- a/mobly/controllers/android_device_lib/adb.py\n+++ b/mobly/controllers/android_device_lib/adb.py\n@@ -138,7 +138,7 @@\n     def __init__(self, serial=''):\n         self.serial = serial\n \n-    def _exec_cmd(self, args, shell, timeout):\n+    def _exec_cmd(self, args, shell, timeout, stderr):\n         \"\"\"Executes adb commands.\n \n         Args:\n@@ -148,6 +148,8 @@\n                 False to invoke it directly. See subprocess.Popen() docs.\n             timeout: float, the number of seconds to wait before timing out.\n                 If not specified, no timeout takes effect.\n+            stderr: a Byte stream, like io.BytesIO, stderr of the command will\n+                be written to this object if provided.\n \n         Returns:\n             The output of the adb command run if exit code is 0.\n@@ -169,6 +171,8 @@\n                 raise AdbTimeoutError(cmd=args, timeout=timeout)\n \n         (out, err) = proc.communicate()\n+        if stderr:\n+            stderr.write(err)\n         ret = proc.returncode\n         logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',\n                       cli_cmd_to_string(args), out, err, ret)\n@@ -177,7 +181,7 @@\n         else:\n             raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)\n \n-    def _exec_adb_cmd(self, name, args, shell, timeout):\n+    def _exec_adb_cmd(self, name, args, shell, timeout, stderr):\n         if shell:\n             # Add quotes around \"adb\" in case the ADB path contains spaces. This\n             # is pretty common on Windows (e.g. Program Files).\n@@ -195,7 +199,9 @@\n                     adb_cmd.append(args)\n                 else:\n                     adb_cmd.extend(args)\n-        return self._exec_cmd(adb_cmd, shell=shell, timeout=timeout)\n+        out = self._exec_cmd(\n+            adb_cmd, shell=shell, timeout=timeout, stderr=stderr)\n+        return out\n \n     def getprop(self, prop_name):\n         \"\"\"Get a property of the device.\n@@ -273,7 +279,7 @@\n         return self.shell(instrumentation_command)\n \n     def __getattr__(self, name):\n-        def adb_call(args=None, shell=False, timeout=None):\n+        def adb_call(args=None, shell=False, timeout=None, stderr=None):\n             \"\"\"Wrapper for an ADB command.\n \n             Args:\n@@ -283,6 +289,8 @@\n                     False to invoke it directly. See subprocess.Proc() docs.\n                 timeout: float, the number of seconds to wait before timing out.\n                     If not specified, no timeout takes effect.\n+                stderr: a Byte stream, like io.BytesIO, stderr of the command\n+                    will be written to this object if provided.\n \n             Returns:\n                 The output of the adb command run if exit code is 0.\n@@ -290,6 +298,6 @@\n             args = args or ''\n             clean_name = name.replace('_', '-')\n             return self._exec_adb_cmd(\n-                clean_name, args, shell=shell, timeout=timeout)\n+                clean_name, args, shell=shell, timeout=timeout, stderr=stderr)\n \n         return adb_call\n", "issue": "Propagate stderr from adb commands\nThe current mobly adb proxy does not propagate stderr if ret code is zero.\r\nWe thought this was ok since Android has fixed return code issues in M.\r\n\r\nBut turns out many China manufacturers did not fix this in China devices.\r\nIn order to better support China devices and potentially other devices of the same ret code problem, we need to surface stderr.\n", "before_files": [{"content": "# Copyright 2016 Google Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom builtins import str\nfrom past.builtins import basestring\n\nimport logging\nimport pipes\nimport psutil\nimport subprocess\nimport threading\n\n# Command to use for running ADB commands.\nADB = 'adb'\n\n# adb gets confused if we try to manage bound ports in parallel, so anything to\n# do with port forwarding must happen under this lock.\nADB_PORT_LOCK = threading.Lock()\n\n# Qualified class name of the default instrumentation test runner.\nDEFAULT_INSTRUMENTATION_RUNNER = 'com.android.common.support.test.runner.AndroidJUnitRunner'\n\n\nclass Error(Exception):\n    \"\"\"Base error type for adb proxy module.\"\"\"\n\n\nclass AdbError(Error):\n    \"\"\"Raised when an adb command encounters an error.\n\n    Args:\n        cmd: list of strings, the adb command executed.\n        stdout: byte string, the raw stdout of the command.\n        stderr: byte string, the raw stderr of the command.\n        ret_code: int, the return code of the command.\n    \"\"\"\n\n    def __init__(self, cmd, stdout, stderr, ret_code):\n        self.cmd = cmd\n        self.stdout = stdout\n        self.stderr = stderr\n        self.ret_code = ret_code\n\n    def __str__(self):\n        return ('Error executing adb cmd \"%s\". ret: %d, stdout: %s, stderr: %s'\n                ) % (cli_cmd_to_string(self.cmd), self.ret_code, self.stdout,\n                     self.stderr)\n\n\nclass AdbTimeoutError(Error):\n    \"\"\"Raised when an command did not complete within expected time.\n\n    Args:\n        cmd: list of strings, the adb command that timed out\n        timeout: float, the number of seconds passed before timing out.\n    \"\"\"\n\n    def __init__(self, cmd, timeout):\n        self.cmd = cmd\n        self.timeout = timeout\n\n    def __str__(self):\n        return 'Timed out executing command \"%s\" after %ss.' % (\n            cli_cmd_to_string(self.cmd), self.timeout)\n\n\ndef list_occupied_adb_ports():\n    \"\"\"Lists all the host ports occupied by adb forward.\n\n    This is useful because adb will silently override the binding if an attempt\n    to bind to a port already used by adb was made, instead of throwing binding\n    error. So one should always check what ports adb is using before trying to\n    bind to a port with adb.\n\n    Returns:\n        A list of integers representing occupied host ports.\n    \"\"\"\n    out = AdbProxy().forward('--list')\n    clean_lines = str(out, 'utf-8').strip().split('\\n')\n    used_ports = []\n    for line in clean_lines:\n        tokens = line.split(' tcp:')\n        if len(tokens) != 3:\n            continue\n        used_ports.append(int(tokens[1]))\n    return used_ports\n\n\ndef cli_cmd_to_string(args):\n    \"\"\"Converts a cmd arg list to string.\n\n    Args:\n        args: list of strings, the arguments of a command.\n\n    Returns:\n        String representation of the command.\n    \"\"\"\n    if isinstance(args, basestring):\n        # Return directly if it's already a string.\n        return args\n    return ' '.join([pipes.quote(arg) for arg in args])\n\n\nclass AdbProxy(object):\n    \"\"\"Proxy class for ADB.\n\n    For syntactic reasons, the '-' in adb commands need to be replaced with\n    '_'. Can directly execute adb commands on an object:\n    >> adb = AdbProxy(<serial>)\n    >> adb.start_server()\n    >> adb.devices() # will return the console output of \"adb devices\".\n\n    By default, command args are expected to be an iterable which is passed\n    directly to subprocess.Popen():\n    >> adb.shell(['echo', 'a', 'b'])\n\n    This way of launching commands is recommended by the subprocess\n    documentation to avoid shell injection vulnerabilities and avoid having to\n    deal with multiple layers of shell quoting and different shell environments\n    between different OSes.\n\n    If you really want to run the command through the system shell, this is\n    possible by supplying shell=True, but try to avoid this if possible:\n    >> adb.shell('cat /foo > /tmp/file', shell=True)\n    \"\"\"\n\n    def __init__(self, serial=''):\n        self.serial = serial\n\n    def _exec_cmd(self, args, shell, timeout):\n        \"\"\"Executes adb commands.\n\n        Args:\n            args: string or list of strings, program arguments.\n                See subprocess.Popen() documentation.\n            shell: bool, True to run this command through the system shell,\n                False to invoke it directly. See subprocess.Popen() docs.\n            timeout: float, the number of seconds to wait before timing out.\n                If not specified, no timeout takes effect.\n\n        Returns:\n            The output of the adb command run if exit code is 0.\n\n        Raises:\n            AdbError: The adb command exit code is not 0.\n            AdbTimeoutError: The adb command timed out.\n        \"\"\"\n        proc = subprocess.Popen(\n            args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)\n        process = psutil.Process(proc.pid)\n        if timeout and timeout <= 0:\n            raise Error('Timeout is not a positive value: %s' % timeout)\n        if timeout and timeout > 0:\n            try:\n                process.wait(timeout=timeout)\n            except psutil.TimeoutExpired:\n                process.terminate()\n                raise AdbTimeoutError(cmd=args, timeout=timeout)\n\n        (out, err) = proc.communicate()\n        ret = proc.returncode\n        logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',\n                      cli_cmd_to_string(args), out, err, ret)\n        if ret == 0:\n            return out\n        else:\n            raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)\n\n    def _exec_adb_cmd(self, name, args, shell, timeout):\n        if shell:\n            # Add quotes around \"adb\" in case the ADB path contains spaces. This\n            # is pretty common on Windows (e.g. Program Files).\n            if self.serial:\n                adb_cmd = '\"%s\" -s \"%s\" %s %s' % (ADB, self.serial, name, args)\n            else:\n                adb_cmd = '\"%s\" %s %s' % (ADB, name, args)\n        else:\n            adb_cmd = [ADB]\n            if self.serial:\n                adb_cmd.extend(['-s', self.serial])\n            adb_cmd.append(name)\n            if args:\n                if isinstance(args, basestring):\n                    adb_cmd.append(args)\n                else:\n                    adb_cmd.extend(args)\n        return self._exec_cmd(adb_cmd, shell=shell, timeout=timeout)\n\n    def getprop(self, prop_name):\n        \"\"\"Get a property of the device.\n\n        This is a convenience wrapper for \"adb shell getprop xxx\".\n\n        Args:\n            prop_name: A string that is the name of the property to get.\n\n        Returns:\n            A string that is the value of the property, or None if the property\n            doesn't exist.\n        \"\"\"\n        return self.shell('getprop %s' % prop_name).decode('utf-8').strip()\n\n    def has_shell_command(self, command):\n        \"\"\"Checks to see if a given check command exists on the device.\n\n        Args:\n            command: A string that is the name of the command to check.\n\n        Returns:\n            A boolean that is True if the command exists and False otherwise.\n        \"\"\"\n        try:\n            output = self.shell(['command', '-v',\n                                 command]).decode('utf-8').strip()\n            return command in output\n        except AdbError:\n            # If the command doesn't exist, then 'command -v' can return\n            # an exit code > 1.\n            return False\n\n    def forward(self, args=None, shell=False):\n        with ADB_PORT_LOCK:\n            return self._exec_adb_cmd('forward', args, shell, timeout=None)\n\n    def instrument(self, package, options=None, runner=None):\n        \"\"\"Runs an instrumentation command on the device.\n\n        This is a convenience wrapper to avoid parameter formatting.\n\n        Example:\n            device.instrument(\n                'com.my.package.test',\n                options = {\n                    'class': 'com.my.package.test.TestSuite',\n                },\n            )\n\n        Args:\n            package: string, the package of the instrumentation tests.\n            options: dict, the instrumentation options including the test\n                class.\n            runner: string, the test runner name, which defaults to\n                DEFAULT_INSTRUMENTATION_RUNNER.\n\n        Returns:\n            The output of instrumentation command.\n        \"\"\"\n        if runner is None:\n            runner = DEFAULT_INSTRUMENTATION_RUNNER\n        if options is None:\n            options = {}\n\n        options_list = []\n        for option_key, option_value in options.items():\n            options_list.append('-e %s %s' % (option_key, option_value))\n        options_string = ' '.join(options_list)\n\n        instrumentation_command = 'am instrument -r -w %s %s/%s' % (\n            options_string, package, runner)\n        logging.info('AndroidDevice|%s: Executing adb shell %s', self.serial,\n                     instrumentation_command)\n        return self.shell(instrumentation_command)\n\n    def __getattr__(self, name):\n        def adb_call(args=None, shell=False, timeout=None):\n            \"\"\"Wrapper for an ADB command.\n\n            Args:\n                args: string or list of strings, arguments to the adb command.\n                    See subprocess.Proc() documentation.\n                shell: bool, True to run this command through the system shell,\n                    False to invoke it directly. See subprocess.Proc() docs.\n                timeout: float, the number of seconds to wait before timing out.\n                    If not specified, no timeout takes effect.\n\n            Returns:\n                The output of the adb command run if exit code is 0.\n            \"\"\"\n            args = args or ''\n            clean_name = name.replace('_', '-')\n            return self._exec_adb_cmd(\n                clean_name, args, shell=shell, timeout=timeout)\n\n        return adb_call\n", "path": "mobly/controllers/android_device_lib/adb.py"}], "after_files": [{"content": "# Copyright 2016 Google Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom builtins import str\nfrom past.builtins import basestring\n\nimport logging\nimport pipes\nimport psutil\nimport subprocess\nimport threading\n\n# Command to use for running ADB commands.\nADB = 'adb'\n\n# adb gets confused if we try to manage bound ports in parallel, so anything to\n# do with port forwarding must happen under this lock.\nADB_PORT_LOCK = threading.Lock()\n\n# Qualified class name of the default instrumentation test runner.\nDEFAULT_INSTRUMENTATION_RUNNER = 'com.android.common.support.test.runner.AndroidJUnitRunner'\n\n\nclass Error(Exception):\n    \"\"\"Base error type for adb proxy module.\"\"\"\n\n\nclass AdbError(Error):\n    \"\"\"Raised when an adb command encounters an error.\n\n    Args:\n        cmd: list of strings, the adb command executed.\n        stdout: byte string, the raw stdout of the command.\n        stderr: byte string, the raw stderr of the command.\n        ret_code: int, the return code of the command.\n    \"\"\"\n\n    def __init__(self, cmd, stdout, stderr, ret_code):\n        self.cmd = cmd\n        self.stdout = stdout\n        self.stderr = stderr\n        self.ret_code = ret_code\n\n    def __str__(self):\n        return ('Error executing adb cmd \"%s\". ret: %d, stdout: %s, stderr: %s'\n                ) % (cli_cmd_to_string(self.cmd), self.ret_code, self.stdout,\n                     self.stderr)\n\n\nclass AdbTimeoutError(Error):\n    \"\"\"Raised when an command did not complete within expected time.\n\n    Args:\n        cmd: list of strings, the adb command that timed out\n        timeout: float, the number of seconds passed before timing out.\n    \"\"\"\n\n    def __init__(self, cmd, timeout):\n        self.cmd = cmd\n        self.timeout = timeout\n\n    def __str__(self):\n        return 'Timed out executing command \"%s\" after %ss.' % (\n            cli_cmd_to_string(self.cmd), self.timeout)\n\n\ndef list_occupied_adb_ports():\n    \"\"\"Lists all the host ports occupied by adb forward.\n\n    This is useful because adb will silently override the binding if an attempt\n    to bind to a port already used by adb was made, instead of throwing binding\n    error. So one should always check what ports adb is using before trying to\n    bind to a port with adb.\n\n    Returns:\n        A list of integers representing occupied host ports.\n    \"\"\"\n    out = AdbProxy().forward('--list')\n    clean_lines = str(out, 'utf-8').strip().split('\\n')\n    used_ports = []\n    for line in clean_lines:\n        tokens = line.split(' tcp:')\n        if len(tokens) != 3:\n            continue\n        used_ports.append(int(tokens[1]))\n    return used_ports\n\n\ndef cli_cmd_to_string(args):\n    \"\"\"Converts a cmd arg list to string.\n\n    Args:\n        args: list of strings, the arguments of a command.\n\n    Returns:\n        String representation of the command.\n    \"\"\"\n    if isinstance(args, basestring):\n        # Return directly if it's already a string.\n        return args\n    return ' '.join([pipes.quote(arg) for arg in args])\n\n\nclass AdbProxy(object):\n    \"\"\"Proxy class for ADB.\n\n    For syntactic reasons, the '-' in adb commands need to be replaced with\n    '_'. Can directly execute adb commands on an object:\n    >> adb = AdbProxy(<serial>)\n    >> adb.start_server()\n    >> adb.devices() # will return the console output of \"adb devices\".\n\n    By default, command args are expected to be an iterable which is passed\n    directly to subprocess.Popen():\n    >> adb.shell(['echo', 'a', 'b'])\n\n    This way of launching commands is recommended by the subprocess\n    documentation to avoid shell injection vulnerabilities and avoid having to\n    deal with multiple layers of shell quoting and different shell environments\n    between different OSes.\n\n    If you really want to run the command through the system shell, this is\n    possible by supplying shell=True, but try to avoid this if possible:\n    >> adb.shell('cat /foo > /tmp/file', shell=True)\n    \"\"\"\n\n    def __init__(self, serial=''):\n        self.serial = serial\n\n    def _exec_cmd(self, args, shell, timeout, stderr):\n        \"\"\"Executes adb commands.\n\n        Args:\n            args: string or list of strings, program arguments.\n                See subprocess.Popen() documentation.\n            shell: bool, True to run this command through the system shell,\n                False to invoke it directly. See subprocess.Popen() docs.\n            timeout: float, the number of seconds to wait before timing out.\n                If not specified, no timeout takes effect.\n            stderr: a Byte stream, like io.BytesIO, stderr of the command will\n                be written to this object if provided.\n\n        Returns:\n            The output of the adb command run if exit code is 0.\n\n        Raises:\n            AdbError: The adb command exit code is not 0.\n            AdbTimeoutError: The adb command timed out.\n        \"\"\"\n        proc = subprocess.Popen(\n            args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)\n        process = psutil.Process(proc.pid)\n        if timeout and timeout <= 0:\n            raise Error('Timeout is not a positive value: %s' % timeout)\n        if timeout and timeout > 0:\n            try:\n                process.wait(timeout=timeout)\n            except psutil.TimeoutExpired:\n                process.terminate()\n                raise AdbTimeoutError(cmd=args, timeout=timeout)\n\n        (out, err) = proc.communicate()\n        if stderr:\n            stderr.write(err)\n        ret = proc.returncode\n        logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',\n                      cli_cmd_to_string(args), out, err, ret)\n        if ret == 0:\n            return out\n        else:\n            raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)\n\n    def _exec_adb_cmd(self, name, args, shell, timeout, stderr):\n        if shell:\n            # Add quotes around \"adb\" in case the ADB path contains spaces. This\n            # is pretty common on Windows (e.g. Program Files).\n            if self.serial:\n                adb_cmd = '\"%s\" -s \"%s\" %s %s' % (ADB, self.serial, name, args)\n            else:\n                adb_cmd = '\"%s\" %s %s' % (ADB, name, args)\n        else:\n            adb_cmd = [ADB]\n            if self.serial:\n                adb_cmd.extend(['-s', self.serial])\n            adb_cmd.append(name)\n            if args:\n                if isinstance(args, basestring):\n                    adb_cmd.append(args)\n                else:\n                    adb_cmd.extend(args)\n        out = self._exec_cmd(\n            adb_cmd, shell=shell, timeout=timeout, stderr=stderr)\n        return out\n\n    def getprop(self, prop_name):\n        \"\"\"Get a property of the device.\n\n        This is a convenience wrapper for \"adb shell getprop xxx\".\n\n        Args:\n            prop_name: A string that is the name of the property to get.\n\n        Returns:\n            A string that is the value of the property, or None if the property\n            doesn't exist.\n        \"\"\"\n        return self.shell('getprop %s' % prop_name).decode('utf-8').strip()\n\n    def has_shell_command(self, command):\n        \"\"\"Checks to see if a given check command exists on the device.\n\n        Args:\n            command: A string that is the name of the command to check.\n\n        Returns:\n            A boolean that is True if the command exists and False otherwise.\n        \"\"\"\n        try:\n            output = self.shell(['command', '-v',\n                                 command]).decode('utf-8').strip()\n            return command in output\n        except AdbError:\n            # If the command doesn't exist, then 'command -v' can return\n            # an exit code > 1.\n            return False\n\n    def forward(self, args=None, shell=False):\n        with ADB_PORT_LOCK:\n            return self._exec_adb_cmd('forward', args, shell, timeout=None)\n\n    def instrument(self, package, options=None, runner=None):\n        \"\"\"Runs an instrumentation command on the device.\n\n        This is a convenience wrapper to avoid parameter formatting.\n\n        Example:\n            device.instrument(\n                'com.my.package.test',\n                options = {\n                    'class': 'com.my.package.test.TestSuite',\n                },\n            )\n\n        Args:\n            package: string, the package of the instrumentation tests.\n            options: dict, the instrumentation options including the test\n                class.\n            runner: string, the test runner name, which defaults to\n                DEFAULT_INSTRUMENTATION_RUNNER.\n\n        Returns:\n            The output of instrumentation command.\n        \"\"\"\n        if runner is None:\n            runner = DEFAULT_INSTRUMENTATION_RUNNER\n        if options is None:\n            options = {}\n\n        options_list = []\n        for option_key, option_value in options.items():\n            options_list.append('-e %s %s' % (option_key, option_value))\n        options_string = ' '.join(options_list)\n\n        instrumentation_command = 'am instrument -r -w %s %s/%s' % (\n            options_string, package, runner)\n        logging.info('AndroidDevice|%s: Executing adb shell %s', self.serial,\n                     instrumentation_command)\n        return self.shell(instrumentation_command)\n\n    def __getattr__(self, name):\n        def adb_call(args=None, shell=False, timeout=None, stderr=None):\n            \"\"\"Wrapper for an ADB command.\n\n            Args:\n                args: string or list of strings, arguments to the adb command.\n                    See subprocess.Proc() documentation.\n                shell: bool, True to run this command through the system shell,\n                    False to invoke it directly. See subprocess.Proc() docs.\n                timeout: float, the number of seconds to wait before timing out.\n                    If not specified, no timeout takes effect.\n                stderr: a Byte stream, like io.BytesIO, stderr of the command\n                    will be written to this object if provided.\n\n            Returns:\n                The output of the adb command run if exit code is 0.\n            \"\"\"\n            args = args or ''\n            clean_name = name.replace('_', '-')\n            return self._exec_adb_cmd(\n                clean_name, args, shell=shell, timeout=timeout, stderr=stderr)\n\n        return adb_call\n", "path": "mobly/controllers/android_device_lib/adb.py"}]}