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