Home | History | Annotate | Download | only in cros
      1 #!/usr/bin/python
      2 
      3 # Copyright 2017 The Chromium OS Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 import argparse
      8 import logging
      9 import pipes
     10 import re
     11 import time
     12 
     13 import common
     14 from autotest_lib.client.bin import utils
     15 from autotest_lib.client.common_lib import logging_config
     16 
     17 _ADB_POLLING_INTERVAL_SECONDS = 10
     18 _ADB_CONNECT_INTERVAL_SECONDS = 1
     19 
     20 
     21 def _get_adb_options(target, socket):
     22     """Get adb global options."""
     23     # ADB 1.0.36 does not support -L adb socket option. Parse the host and port
     24     # part from the socket instead.
     25     # https://developer.android.com/studio/command-line/adb.html#issuingcommands
     26     pattern = r'^[^:]+:([^:]+):(\d+)$'
     27     match = re.match(pattern, socket)
     28     if not match:
     29         raise ValueError('Unrecognized socket format: %s' % socket)
     30     server_host, server_port = match.groups()
     31     return '-s %s -H %s -P %s' % (
     32         pipes.quote(target), pipes.quote(server_host), pipes.quote(server_port))
     33 
     34 
     35 def _run_adb_cmd(cmd, adb_option="", **kwargs):
     36     """Run adb command.
     37 
     38     @param cmd: command to issue with adb. (Ex: connect, devices)
     39     @param target: Device to connect to.
     40     @param adb_option: adb global option configuration.
     41 
     42     @return: the stdout of the command.
     43     """
     44     adb_cmd = 'adb %s %s' % (adb_option, cmd)
     45     output = utils.system_output(adb_cmd, **kwargs)
     46     logging.debug('%s: %s', adb_cmd, output)
     47     return output
     48 
     49 
     50 def _is_adb_connected(target, adb_option=""):
     51     """Return true if adb is connected to the container.
     52 
     53     @param target: Device to connect to.
     54     @param adb_option: adb global option configuration.
     55     """
     56     output = _run_adb_cmd(
     57         'get-state', adb_option=adb_option, ignore_status=True)
     58     return output.strip() == 'device'
     59 
     60 
     61 def _ensure_adb_connected(target, adb_option=""):
     62     """Ensures adb is connected to the container, reconnects otherwise.
     63 
     64     @param target: Device to connect to.
     65     @param adb_option: adb global options configuration.
     66     """
     67     while not _is_adb_connected(target, adb_option):
     68         logging.info('adb not connected. attempting to reconnect')
     69         _run_adb_cmd('connect %s' % pipes.quote(target),
     70                      adb_option=adb_option, ignore_status=True)
     71         time.sleep(_ADB_CONNECT_INTERVAL_SECONDS)
     72 
     73 
     74 if __name__ == '__main__':
     75     logging_config.LoggingConfig().configure_logging(verbose=True)
     76     parser = argparse.ArgumentParser(description='ensure adb is connected')
     77     parser.add_argument('target', help='Device to connect to')
     78     parser.add_argument('--socket', help='ADB server socket.',
     79                         default='tcp:localhost:5037')
     80     args = parser.parse_args()
     81     adb_option = _get_adb_options(args.target, args.socket)
     82 
     83     logging.info('Starting adb_keepalive for target %s on socket %s',
     84                  args.target, args.socket)
     85     while True:
     86         try:
     87             time.sleep(_ADB_POLLING_INTERVAL_SECONDS)
     88             _ensure_adb_connected(args.target, adb_option=adb_option)
     89         except KeyboardInterrupt:
     90             logging.info('Shutting down')
     91             break
     92