• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2#   Copyright 2016 - The Android Open Source Project
3#
4#   Licensed under the Apache License, Version 2.0 (the "License");
5#   you may not use this file except in compliance with the License.
6#   You may obtain a copy of the License at
7#
8#       http://www.apache.org/licenses/LICENSE-2.0
9#
10#   Unless required by applicable law or agreed to in writing, software
11#   distributed under the License is distributed on an "AS IS" BASIS,
12#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13#   See the License for the specific language governing permissions and
14#   limitations under the License.
15
16from builtins import str
17
18import logging
19import random
20import socket
21import subprocess
22import time
23
24
25class AdbError(Exception):
26    """Raised when there is an error in adb operations."""
27
28    def __init__(self, cmd, stdout, stderr, ret_code):
29        self.cmd = cmd
30        self.stdout = stdout
31        self.stderr = stderr
32        self.ret_code = ret_code
33
34    def __str__(self):
35        return ("Error executing adb cmd '%s'. ret: %d, stdout: %s, stderr: %s"
36                ) % (self.cmd, self.ret_code, self.stdout, self.stderr)
37
38
39SL4A_LAUNCH_CMD = (
40    "am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER "
41    "--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT {} "
42    "com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher")
43
44
45def get_available_host_port():
46    """Gets a host port number available for adb forward.
47
48    Returns:
49        An integer representing a port number on the host available for adb
50        forward.
51    """
52    while True:
53        port = random.randint(1024, 9900)
54        if is_port_available(port):
55            return port
56
57
58def is_port_available(port):
59    """Checks if a given port number is available on the system.
60
61    Args:
62        port: An integer which is the port number to check.
63
64    Returns:
65        True if the port is available; False otherwise.
66    """
67    # Make sure adb is not using this port so we don't accidentally interrupt
68    # ongoing runs by trying to bind to the port.
69    if port in list_occupied_adb_ports():
70        return False
71    s = None
72    try:
73        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
74        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
75        s.bind(('localhost', port))
76        return True
77    except socket.error:
78        return False
79    finally:
80        if s:
81            s.close()
82
83
84def list_occupied_adb_ports():
85    """Lists all the host ports occupied by adb forward.
86
87    This is useful because adb will silently override the binding if an attempt
88    to bind to a port already used by adb was made, instead of throwing binding
89    error. So one should always check what ports adb is using before trying to
90    bind to a port with adb.
91
92    Returns:
93        A list of integers representing occupied host ports.
94    """
95    out = AdbProxy().forward("--list")
96    clean_lines = str(out, 'utf-8').strip().split('\n')
97    used_ports = []
98    for line in clean_lines:
99        tokens = line.split(" tcp:")
100        if len(tokens) != 3:
101            continue
102        used_ports.append(int(tokens[1]))
103    return used_ports
104
105
106class AdbProxy():
107    """Proxy class for ADB.
108
109    For syntactic reasons, the '-' in adb commands need to be replaced with
110    '_'. Can directly execute adb commands on an object:
111    >> adb = AdbProxy(<serial>)
112    >> adb.start_server()
113    >> adb.devices() # will return the console output of "adb devices".
114    """
115
116    def __init__(self, serial="", log=None):
117        self.serial = serial
118        if serial:
119            self.adb_str = "adb -s {}".format(serial)
120        else:
121            self.adb_str = "adb"
122        self.log = log
123
124    def _exec_cmd(self, cmd):
125        """Executes adb commands in a new shell.
126
127        This is specific to executing adb binary because stderr is not a good
128        indicator of cmd execution status.
129
130        Args:
131            cmd: string, the adb command to execute.
132
133        Returns:
134            The output of the adb command run if the exit code is 0.
135
136        Raises:
137            AdbError if the adb command exit code is not 0.
138        """
139        proc = subprocess.Popen(cmd,
140                                stdout=subprocess.PIPE,
141                                stderr=subprocess.PIPE,
142                                shell=True)
143        (out, err) = proc.communicate()
144        ret = proc.returncode
145        logging.debug("cmd: %s, stdout: %s, stderr: %s, ret: %s", cmd, out,
146                      err, ret)
147        if ret == 0:
148            return out
149        else:
150            raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret)
151
152    def _exec_adb_cmd(self, name, arg_str):
153        return self._exec_cmd(' '.join((self.adb_str, name, arg_str)))
154
155    def tcp_forward(self, host_port, device_port):
156        """Starts TCP forwarding.
157
158        Args:
159            host_port: Port number to use on the computer.
160            device_port: Port number to use on the android device.
161        """
162        self.forward("tcp:{} tcp:{}".format(host_port, device_port))
163
164    def reverse_tcp_forward(self, device_port, host_port):
165        """Starts reverse TCP forwarding.
166
167        Args:
168            device_port: Port number to use on the android device.
169            host_port: Port number to use on the computer.
170        """
171        self.reverse("tcp:{} tcp:{}".format(device_port, host_port))
172
173    def __getattr__(self, name):
174
175        def adb_call(*args):
176            clean_name = name.replace('_', '-')
177            arg_str = ' '.join(str(elem) for elem in args)
178            return self._exec_adb_cmd(clean_name, arg_str)
179
180        return adb_call
181