Home | History | Annotate | Download | only in controllers
      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 
     16 from builtins import str
     17 
     18 import logging
     19 import random
     20 import socket
     21 import subprocess
     22 import time
     23 
     24 from vts.runners.host import const
     25 
     26 
     27 class AdbError(Exception):
     28     """Raised when there is an error in adb operations."""
     29 
     30     def __init__(self, cmd, stdout, stderr, ret_code):
     31         self.cmd = cmd
     32         self.stdout = stdout
     33         self.stderr = stderr
     34         self.ret_code = ret_code
     35 
     36     def __str__(self):
     37         return ("Error executing adb cmd '%s'. ret: %d, stdout: %s, stderr: %s"
     38                 ) % (self.cmd, self.ret_code, self.stdout, self.stderr)
     39 
     40 
     41 SL4A_LAUNCH_CMD = (
     42     "am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER "
     43     "--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT {} "
     44     "com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher")
     45 
     46 
     47 def get_available_host_port():
     48     """Gets a host port number available for adb forward.
     49 
     50     Returns:
     51         An integer representing a port number on the host available for adb
     52         forward.
     53     """
     54     while True:
     55         port = random.randint(1024, 9900)
     56         if is_port_available(port):
     57             return port
     58 
     59 
     60 def is_port_available(port):
     61     """Checks if a given port number is available on the system.
     62 
     63     Args:
     64         port: An integer which is the port number to check.
     65 
     66     Returns:
     67         True if the port is available; False otherwise.
     68     """
     69     # Make sure adb is not using this port so we don't accidentally interrupt
     70     # ongoing runs by trying to bind to the port.
     71     if port in list_occupied_adb_ports():
     72         return False
     73     s = None
     74     try:
     75         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     76         s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     77         s.bind(('localhost', port))
     78         return True
     79     except socket.error:
     80         return False
     81     finally:
     82         if s:
     83             s.close()
     84 
     85 
     86 def list_occupied_adb_ports():
     87     """Lists all the host ports occupied by adb forward.
     88 
     89     This is useful because adb will silently override the binding if an attempt
     90     to bind to a port already used by adb was made, instead of throwing binding
     91     error. So one should always check what ports adb is using before trying to
     92     bind to a port with adb.
     93 
     94     Returns:
     95         A list of integers representing occupied host ports.
     96     """
     97     out = AdbProxy().forward("--list")
     98     clean_lines = str(out, 'utf-8').strip().split('\n')
     99     used_ports = []
    100     for line in clean_lines:
    101         tokens = line.split(" tcp:")
    102         if len(tokens) != 3:
    103             continue
    104         used_ports.append(int(tokens[1]))
    105     return used_ports
    106 
    107 
    108 class AdbProxy():
    109     """Proxy class for ADB.
    110 
    111     For syntactic reasons, the '-' in adb commands need to be replaced with
    112     '_'. Can directly execute adb commands on an object:
    113     >> adb = AdbProxy(<serial>)
    114     >> adb.start_server()
    115     >> adb.devices() # will return the console output of "adb devices".
    116     """
    117 
    118     def __init__(self, serial="", log=None):
    119         self.serial = serial
    120         if serial:
    121             self.adb_str = "adb -s {}".format(serial)
    122         else:
    123             self.adb_str = "adb"
    124         self.log = log
    125 
    126     def _exec_cmd(self, cmd, no_except=False):
    127         """Executes adb commands in a new shell.
    128 
    129         This is specific to executing adb binary because stderr is not a good
    130         indicator of cmd execution status.
    131 
    132         Args:
    133             cmd: string, the adb command to execute.
    134             no_except: bool, controls whether exception can be thrown.
    135 
    136         Returns:
    137             The output of the adb command run if the exit code is 0 and if
    138             exceptions are allowed. Otherwise, returns a dictionary containing
    139             stdout, stderr, and exit code.
    140 
    141         Raises:
    142             AdbError if the adb command exit code is not 0 and exceptions are
    143             allowed.
    144         """
    145         proc = subprocess.Popen(cmd,
    146                                 stdout=subprocess.PIPE,
    147                                 stderr=subprocess.PIPE,
    148                                 shell=True)
    149         (out, err) = proc.communicate()
    150         ret = proc.returncode
    151         logging.debug("cmd: %s, stdout: %s, stderr: %s, ret: %s", cmd, out,
    152                       err, ret)
    153         if no_except:
    154             return {
    155                 const.STDOUT: out,
    156                 const.STDERR: err,
    157                 const.EXIT_CODE: ret,
    158             }
    159         else:
    160             if ret == 0:
    161                 return out
    162             else:
    163                 raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret)
    164 
    165     def tcp_forward(self, host_port, device_port):
    166         """Starts TCP forwarding.
    167 
    168         Args:
    169             host_port: Port number to use on the computer.
    170             device_port: Port number to use on the android device.
    171         """
    172         self.forward("tcp:{} tcp:{}".format(host_port, device_port))
    173 
    174     def reverse_tcp_forward(self, device_port, host_port):
    175         """Starts reverse TCP forwarding.
    176 
    177         Args:
    178             device_port: Port number to use on the android device.
    179             host_port: Port number to use on the computer.
    180         """
    181         self.reverse("tcp:{} tcp:{}".format(device_port, host_port))
    182 
    183     def __getattr__(self, name):
    184 
    185         def adb_call(*args, **kwargs):
    186             clean_name = name.replace('_', '-')
    187             arg_str = ' '.join(str(elem) for elem in args)
    188             return self._exec_cmd(' '.join((self.adb_str, clean_name, arg_str)),
    189                                   kwargs)
    190 
    191         return adb_call
    192