1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import optparse 6import os 7import random 8import string 9import subprocess 10import sys 11 12from devil.android.constants import chrome 13 14class OptionParserIgnoreErrors(optparse.OptionParser): 15 """Wrapper for OptionParser that ignores errors and produces no output.""" 16 17 def error(self, msg): 18 pass 19 20 def exit(self, status=0, msg=None): 21 pass 22 23 def print_usage(self, out_file=None): 24 pass 25 26 def print_help(self, out_file=None): 27 pass 28 29 def print_version(self, out_file=None): 30 pass 31 32 33def add_adb_serial(adb_command, device_serial): 34 """Add serial number to ADB shell command. 35 36 ADB shell command is given as list, e.g. 37 ['adb','shell','some_command','some_args']. 38 This replaces it with: 39 ['adb','shell',-s',device_serial,'some_command','some_args'] 40 41 Args: 42 adb_command: ADB command list. 43 device_serial: Device serial number. 44 45 Returns: 46 ADB command list with serial number added. 47 """ 48 if device_serial is not None: 49 adb_command.insert(1, device_serial) 50 adb_command.insert(1, '-s') 51 52 53def construct_adb_shell_command(shell_args, device_serial): 54 """Construct an ADB shell command with given device serial and arguments. 55 56 Args: 57 shell_args: array of arguments to pass to adb shell. 58 device_serial: if not empty, will add the appropriate command-line 59 parameters so that adb targets the given device. 60 """ 61 adb_command = ['adb', 'shell', ' '.join(shell_args)] 62 add_adb_serial(adb_command, device_serial) 63 return adb_command 64 65 66def run_adb_command(adb_command): 67 adb_output = [] 68 adb_return_code = 0 69 try: 70 adb_output = subprocess.check_output(adb_command, stderr=subprocess.STDOUT, 71 shell=False, universal_newlines=True) 72 except OSError as error: 73 # This usually means that the adb executable was not found in the path. 74 print >> sys.stderr, ('\nThe command "%s" failed with the following error:' 75 % ' '.join(adb_command)) 76 print >> sys.stderr, ' %s' % str(error) 77 print >> sys.stderr, 'Is adb in your path?' 78 adb_return_code = error.errno 79 adb_output = error 80 except subprocess.CalledProcessError as error: 81 # The process exited with an error. 82 adb_return_code = error.returncode 83 adb_output = error.output 84 85 return (adb_output, adb_return_code) 86 87 88def run_adb_shell(shell_args, device_serial): 89 """Runs "adb shell" with the given arguments. 90 91 Args: 92 shell_args: array of arguments to pass to adb shell. 93 device_serial: if not empty, will add the appropriate command-line 94 parameters so that adb targets the given device. 95 Returns: 96 A tuple containing the adb output (stdout & stderr) and the return code 97 from adb. Will exit if adb fails to start. 98 """ 99 adb_command = construct_adb_shell_command(shell_args, device_serial) 100 return run_adb_command(adb_command) 101 102 103def get_device_sdk_version(): 104 """Uses adb to attempt to determine the SDK version of a running device.""" 105 106 getprop_args = ['getprop', 'ro.build.version.sdk'] 107 108 # get_device_sdk_version() is called before we even parse our command-line 109 # args. Therefore, parse just the device serial number part of the 110 # command-line so we can send the adb command to the correct device. 111 parser = OptionParserIgnoreErrors() 112 parser.add_option('-e', '--serial', dest='device_serial', type='string') 113 options, unused_args = parser.parse_args() # pylint: disable=unused-variable 114 115 success = False 116 117 adb_output, adb_return_code = run_adb_shell(getprop_args, 118 options.device_serial) 119 120 if adb_return_code == 0: 121 # ADB may print output other than the version number (e.g. it chould 122 # print a message about starting the ADB server). 123 # Break the ADB output into white-space delimited segments. 124 parsed_output = str.split(adb_output) 125 if parsed_output: 126 # Assume that the version number is the last thing printed by ADB. 127 version_string = parsed_output[-1] 128 if version_string: 129 try: 130 # Try to convert the text into an integer. 131 version = int(version_string) 132 except ValueError: 133 version = -1 134 else: 135 success = True 136 137 if not success: 138 sys.exit(1) 139 140 return version 141 142 143def generate_random_filename_for_test(): 144 """Used for temporary files used in tests. 145 146 Files created from 'NamedTemporaryFile' have inconsistent reuse support across 147 platforms, so it's not guaranteed that they can be reopened. Since many tests 148 communicate files via path, we typically use this method, as well as 149 manual file removal.""" 150 name = ''.join(random.choice(string.ascii_uppercase + 151 string.digits) for _ in range(10)) 152 return os.path.abspath(name) 153 154 155def get_supported_browsers(): 156 """Returns the package names of all supported browsers.""" 157 # Add aliases for backwards compatibility. 158 supported_browsers = { 159 'stable': chrome.PACKAGE_INFO['chrome_stable'], 160 'beta': chrome.PACKAGE_INFO['chrome_beta'], 161 'dev': chrome.PACKAGE_INFO['chrome_dev'], 162 'build': chrome.PACKAGE_INFO['chrome'], 163 } 164 supported_browsers.update(chrome.PACKAGE_INFO) 165 return supported_browsers 166 167 168def get_default_serial(): 169 if 'ANDROID_SERIAL' in os.environ: 170 return os.environ['ANDROID_SERIAL'] 171 return None 172 173 174def get_main_options(parser): 175 parser.add_option('-o', dest='output_file', help='write trace output to FILE', 176 default=None, metavar='FILE') 177 parser.add_option('-t', '--time', dest='trace_time', type='int', 178 help='trace for N seconds', metavar='N') 179 parser.add_option('-j', '--json', dest='write_json', 180 default=False, action='store_true', 181 help='write a JSON file') 182 parser.add_option('--link-assets', dest='link_assets', default=False, 183 action='store_true', 184 help='(deprecated)') 185 parser.add_option('--from-file', dest='from_file', action='store', 186 help='read the trace from a file (compressed) rather than' 187 'running a live trace') 188 parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', 189 type='string', help='(deprecated)') 190 parser.add_option('-e', '--serial', dest='device_serial_number', 191 default=get_default_serial(), 192 type='string', help='adb device serial number') 193 parser.add_option('--target', dest='target', default='android', type='string', 194 help='choose tracing target (android or linux)') 195 parser.add_option('--timeout', dest='timeout', type='int', 196 help='timeout for start and stop tracing (seconds)') 197 parser.add_option('--collection-timeout', dest='collection_timeout', 198 type='int', help='timeout for data collection (seconds)') 199 parser.add_option('-a', '--app', dest='app_name', default=None, 200 type='string', action='store', 201 help='enable application-level tracing for ' 202 'comma-separated list of app cmdlines') 203 parser.add_option('-t', '--time', dest='trace_time', type='int', 204 help='trace for N seconds', metavar='N') 205 parser.add_option('--target', dest='target', default='android', 206 type='string', help='choose tracing target (android or ' 207 ' linux)') 208 parser.add_option('-b', '--buf-size', dest='trace_buf_size', 209 type='int', help='use a trace buffer size ' 210 ' of N KB', metavar='N') 211 return parser 212