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