1dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com#!/usr/bin/python 2dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# 3dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# Copyright (c) 2012 The Chromium Authors. All rights reserved. 4dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# Use of this source code is governed by a BSD-style license that can be 5dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# found in the LICENSE file. 6dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 7dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com""" adb_list_devices: list information about attached Android devices. """ 8dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 9dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 10dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comimport os 11dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comimport re 12dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comimport shlex 13dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comimport subprocess 14dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comimport sys 15dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 16dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# This file, which resides on every Android device, contains a great deal of 17dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# information about the device. 18dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comINFO_FILE = '/system/build.prop' 19dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 20dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com# Default set of properties to query about a device. 21dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comDEFAULT_PROPS_TO_GET = ['ro.product.device', 'ro.build.version.release', 22dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 'ro.build.type'] 23dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 24dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 25dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comdef GetDeviceInfo(adb, serial, props_to_get): 26dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ Return a list of values (or "<Unknown>" if no value can be found) for the 27dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com given set of properties for the device with the given serial number. 28dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 29dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb: path to the ADB program. 30dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com serial: serial number of the target device. 31dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com props_to_get: list of strings indicating which properties to determine. 32dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ 33dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device_proc = subprocess.Popen([adb, '-s', serial, 'shell', 'cat', 34dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com INFO_FILE], stdout=subprocess.PIPE) 35dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com code = device_proc.wait() 36dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if code != 0: 37dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com raise Exception('Could not query device with serial number %s.' % serial) 38dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com output = device_proc.stdout.read() 39dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device_info = [] 40dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com for prop in props_to_get: 41dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com # Find the property in the outputs 42dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com search_str = r'%s=(\S+)' % prop 43dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com match = re.search(search_str, output) 44dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if not match: 45dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com value = '<Unknown>' 46dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com else: 47dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com value = match.group(1) 48dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device_info.append(value) 49dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return device_info 50dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 51dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 52dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comdef PrintPrettyTable(data, file=None): 53dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ Print out the given data in a nicely-spaced format. This function scans 54dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com the list multiple times and uses extra memory, so don't use it for big data 55dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com sets. 56dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 57dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com data: list of lists of strings, where each list represents a row of data. 58dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com This table is assumed to be rectangular; if the length of any list differs 59dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com some of the output may not get printed. 60dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com file: file-like object into which the table should be written. If none is 61dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com provided, the table is written to stdout. 62dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ 63dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if not file: 64dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com file = sys.stdout 65dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com column_widths = [0 for length in data[0]] 66dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com for line in data: 67dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com column_widths = [max(longest_len, len(prop)) for \ 68dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com longest_len, prop in zip(column_widths, line)] 69dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com for line in data: 70dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com for prop, width in zip(line, column_widths): 71dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com file.write(prop.ljust(width + 1)) 72dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com file.write('\n') 73dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 74dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 75dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comdef FindADB(hint=None): 76dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ Attempt to find the ADB program using the following sequence of steps. 77dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com Returns the path to ADB if it can be found, or None otherwise. 78dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 1. If a hint was provided, is it a valid path to ADB? 79dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 2. Is ADB in PATH? 80dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 3. Is there an environment variable for ADB? 81dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 4. If the ANDROID_SDK_ROOT variable is set, try to find ADB in the SDK 82dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com directory. 83dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 84dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com hint: string indicating a possible path to ADB. 85dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ 86dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com # 1. If a hint was provided, does it point to ADB? 87dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if hint: 88dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if os.path.basename(hint) == 'adb': 89dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = hint 90dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com else: 91dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = os.path.join(hint, 'adb') 92dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if subprocess.Popen([adb, 'version'], stdout=subprocess.PIPE).wait() == 0: 93dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return adb 94dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 95dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com # 2. Is 'adb' in our PATH? 96dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = 'adb' 97dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if subprocess.Popen([adb, 'version'], stdout=subprocess.PIPE).wait() == 0: 98dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return adb 99dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 100dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com # 3. Is there an environment variable for ADB? 101dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com try: 102dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = os.environ.get('ADB') 103dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if subprocess.Popen([adb, 'version'], stdout=subprocess.PIPE).wait() == 0: 104dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return adb 105dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com except: 106dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com pass 107dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 108dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com # 4. If ANDROID_SDK_ROOT is set, try to find ADB in the SDK directory. 109dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com try: 110dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com sdk_dir = os.environ.get('ANDROID_SDK_ROOT') 111dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = os.path.join(sdk_dir, 'platform-tools', 'adb') 112dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if subprocess.Popen([adb, 'version'], stdout=subprocess.PIPE).wait() == 0: 113dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return adb 114dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com except: 115dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com pass 116dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com return None 117dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 118dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 119dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comdef main(argv): 120dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ Print out information about connected Android devices. By default, print 121dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com the serial number, status, device name, OS version, and build type of each 122dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device. If any arguments are supplied on the command line, print the serial 123dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com number and status for each device along with values for those arguments 124dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com interpreted as properties. 125dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com """ 126dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if len(argv) > 1: 127dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com props_to_get = argv[1:] 128dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com else: 129dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com props_to_get = DEFAULT_PROPS_TO_GET 130dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com adb = FindADB() 131dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if not adb: 132dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com raise Exception('Could not find ADB!') 133dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com proc = subprocess.Popen([adb, 'devices'], stdout=subprocess.PIPE) 134dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com code = proc.wait() 135dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if code != 0: 136dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com raise Exception('Failure in ADB: could not find attached devices.') 137dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com header = ['Serial', 'Status'] 138dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com header.extend(props_to_get) 139dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com output_lines = [header] 140dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com for line in proc.stdout: 141dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com line = line.rstrip() 142dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com if line != 'List of devices attached' and line != '': 143dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com line_list = shlex.split(line) 144dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com serial = line_list[0] 145dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com status = line_list[1] 146dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device_info = [serial, status] 147dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com device_info.extend(GetDeviceInfo(adb, serial, props_to_get)) 148dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com output_lines.append(device_info) 149dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com PrintPrettyTable(output_lines) 150dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 151dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com 152dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.comif __name__ == '__main__': 153dcdd57faf02fb4fd23bb8265392b9c22e068907edjsollen@google.com sys.exit(main(sys.argv))