1# -*- coding: utf-8 -*- 2 3#------------------------------------------------------------------------- 4# drawElements Quality Program utilities 5# -------------------------------------- 6# 7# Copyright 2017 The Android Open Source Project 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20# 21#------------------------------------------------------------------------- 22 23import os 24import re 25import sys 26import argparse 27import threading 28import subprocess 29 30from build_apk import findSDK 31from build_apk import getDefaultBuildRoot 32from build_apk import getPackageAndLibrariesForTarget 33from build_apk import getBuildRootRelativeAPKPath 34from build_apk import parsePackageName 35 36# Import from <root>/scripts 37sys.path.append(os.path.join(os.path.dirname(__file__), "..")) 38 39from build.common import * 40 41class Device: 42 def __init__(self, serial, product, model, device): 43 self.serial = serial 44 self.product = product 45 self.model = model 46 self.device = device 47 48 def __str__ (self): 49 return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device) 50 51def getDevices (adbPath): 52 proc = subprocess.Popen([adbPath, 'devices', '-l'], stdout=subprocess.PIPE) 53 (stdout, stderr) = proc.communicate() 54 55 if proc.returncode != 0: 56 raise Exception("adb devices -l failed, got %d" % proc.returncode) 57 58 ptrn = re.compile(r'^([a-zA-Z0-9\.\-:]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)') 59 devices = [] 60 for line in stdout.splitlines()[1:]: 61 if len(line.strip()) == 0: 62 continue 63 64 m = ptrn.match(line) 65 if m == None: 66 print "WARNING: Failed to parse device info '%s'" % line 67 continue 68 69 devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4))) 70 71 return devices 72 73def execWithPrintPrefix (args, linePrefix="", failOnNonZeroExit=True): 74 75 def readApplyPrefixAndPrint (source, prefix, sink): 76 while True: 77 line = source.readline() 78 if len(line) == 0: # EOF 79 break; 80 sink.write(prefix + line) 81 82 process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 83 stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout)) 84 stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr)) 85 stdoutJob.start() 86 stderrJob.start() 87 retcode = process.wait() 88 if failOnNonZeroExit and retcode != 0: 89 raise Exception("Failed to execute '%s', got %d" % (str(args), retcode)) 90 91def serialApply (f, argsList): 92 for args in argsList: 93 f(*args) 94 95def parallelApply (f, argsList): 96 class ErrorCode: 97 def __init__ (self): 98 self.error = None; 99 100 def applyAndCaptureError (func, args, errorCode): 101 try: 102 func(*args) 103 except: 104 errorCode.error = sys.exc_info() 105 106 errorCode = ErrorCode() 107 jobs = [] 108 for args in argsList: 109 job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode)) 110 job.start() 111 jobs.append(job) 112 113 for job in jobs: 114 job.join() 115 116 if errorCode.error: 117 raise errorCode.error[0], errorCode.error[1], errorCode.error[2] 118 119def uninstall (adbPath, packageName, extraArgs = [], printPrefix=""): 120 print printPrefix + "Removing existing %s...\n" % packageName, 121 execWithPrintPrefix([adbPath] + extraArgs + [ 122 'uninstall', 123 packageName 124 ], printPrefix, failOnNonZeroExit=False) 125 print printPrefix + "Remove complete\n", 126 127def install (adbPath, apkPath, extraArgs = [], printPrefix=""): 128 print printPrefix + "Installing %s...\n" % apkPath, 129 execWithPrintPrefix([adbPath] + extraArgs + [ 130 'install', 131 apkPath 132 ], printPrefix) 133 print printPrefix + "Install complete\n", 134 135def installToDevice (device, adbPath, packageName, apkPath, printPrefix=""): 136 if len(printPrefix) == 0: 137 print "Installing to %s (%s)...\n" % (device.serial, device.model), 138 else: 139 print printPrefix + "Installing to %s\n" % device.serial, 140 141 uninstall(adbPath, packageName, ['-s', device.serial], printPrefix) 142 install(adbPath, apkPath, ['-s', device.serial], printPrefix) 143 144def installToDevices (devices, doParallel, adbPath, packageName, apkPath): 145 padLen = max([len(device.model) for device in devices])+1 146 if doParallel: 147 parallelApply(installToDevice, [(device, adbPath, packageName, apkPath, ("(%s):%s" % (device.model, ' ' * (padLen - len(device.model))))) for device in devices]); 148 else: 149 serialApply(installToDevice, [(device, adbPath, packageName, apkPath) for device in devices]); 150 151def installToAllDevices (doParallel, adbPath, packageName, apkPath): 152 devices = getDevices(adbPath) 153 installToDevices(devices, doParallel, adbPath, packageName, apkPath) 154 155def getAPKPath (buildRootPath, target): 156 package = getPackageAndLibrariesForTarget(target)[0] 157 return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package)) 158 159def getPackageName (target): 160 package = getPackageAndLibrariesForTarget(target)[0] 161 manifestPath = os.path.join(DEQP_DIR, "android", package.appDirName, "AndroidManifest.xml") 162 163 return parsePackageName(manifestPath) 164 165def findADB (): 166 adbInPath = which("adb") 167 if adbInPath != None: 168 return adbInPath 169 170 sdkPath = findSDK() 171 if sdkPath != None: 172 adbInSDK = os.path.join(sdkPath, "platform-tools", "adb") 173 if os.path.isfile(adbInSDK): 174 return adbInSDK 175 176 return None 177 178def parseArgs (): 179 defaultADBPath = findADB() 180 defaultBuildRoot = getDefaultBuildRoot() 181 182 parser = argparse.ArgumentParser(os.path.basename(__file__), 183 formatter_class=argparse.ArgumentDefaultsHelpFormatter) 184 parser.add_argument('--build-root', 185 dest='buildRoot', 186 default=defaultBuildRoot, 187 help="Root build directory") 188 parser.add_argument('--adb', 189 dest='adbPath', 190 default=defaultADBPath, 191 help="ADB binary path", 192 required=(True if defaultADBPath == None else False)) 193 parser.add_argument('--target', 194 dest='target', 195 help='Build target', 196 choices=['deqp', 'openglcts'], 197 default='deqp') 198 parser.add_argument('-p', '--parallel', 199 dest='doParallel', 200 action="store_true", 201 help="Install package in parallel") 202 parser.add_argument('-s', '--serial', 203 dest='serial', 204 type=str, 205 nargs='+', 206 help="Install package to device with serial number") 207 parser.add_argument('-a', '--all', 208 dest='all', 209 action="store_true", 210 help="Install to all devices") 211 212 return parser.parse_args() 213 214if __name__ == "__main__": 215 args = parseArgs() 216 packageName = getPackageName(args.target) 217 apkPath = getAPKPath(args.buildRoot, args.target) 218 219 if not os.path.isfile(apkPath): 220 die("%s does not exist" % apkPath) 221 222 if args.all: 223 installToAllDevices(args.doParallel, args.adbPath, packageName, apkPath) 224 else: 225 if args.serial == None: 226 devices = getDevices(args.adbPath) 227 if len(devices) == 0: 228 die('No devices connected') 229 elif len(devices) == 1: 230 installToDevice(devices[0], args.adbPath, packageName, apkPath) 231 else: 232 print "More than one device connected:" 233 for i in range(0, len(devices)): 234 print "%3d: %16s %s" % ((i+1), devices[i].serial, devices[i].model) 235 236 deviceNdx = int(raw_input("Choose device (1-%d): " % len(devices))) 237 installToDevice(devices[deviceNdx-1], args.adbPath, packageName, apkPath) 238 else: 239 devices = getDevices(args.adbPath) 240 241 devices = [dev for dev in devices if dev.serial in args.serial] 242 devSerials = [dev.serial for dev in devices] 243 notFounds = [serial for serial in args.serial if not serial in devSerials] 244 245 for notFound in notFounds: 246 print("Couldn't find device matching serial '%s'" % notFound) 247 248 installToDevices(devices, args.doParallel, args.adbPath, packageName, apkPath) 249