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 ctsbuild.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.decode('utf-8')) 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.decode('utf-8')) 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]).with_traceback(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 '-g', 132 apkPath 133 ], printPrefix) 134 print(printPrefix + "Install complete\n",) 135 136def installToDevice (device, adbPath, packageName, apkPath, printPrefix=""): 137 if len(printPrefix) == 0: 138 print("Installing to %s (%s)...\n" % (device.serial, device.model), end='') 139 else: 140 print(printPrefix + "Installing to %s\n" % device.serial, end='') 141 142 uninstall(adbPath, packageName, ['-s', device.serial], printPrefix) 143 install(adbPath, apkPath, ['-s', device.serial], printPrefix) 144 145def installToDevices (devices, doParallel, adbPath, packageName, apkPath): 146 padLen = max([len(device.model) for device in devices])+1 147 if doParallel: 148 parallelApply(installToDevice, [(device, adbPath, packageName, apkPath, ("(%s):%s" % (device.model, ' ' * (padLen - len(device.model))))) for device in devices]); 149 else: 150 serialApply(installToDevice, [(device, adbPath, packageName, apkPath) for device in devices]); 151 152def installToAllDevices (doParallel, adbPath, packageName, apkPath): 153 devices = getDevices(adbPath) 154 installToDevices(devices, doParallel, adbPath, packageName, apkPath) 155 156def getAPKPath (buildRootPath, target): 157 package = getPackageAndLibrariesForTarget(target)[0] 158 return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package)) 159 160def getPackageName (target): 161 package = getPackageAndLibrariesForTarget(target)[0] 162 manifestPath = os.path.join(DEQP_DIR, "android", package.appDirName, "AndroidManifest.xml") 163 164 return parsePackageName(manifestPath) 165 166def findADB (): 167 adbInPath = which("adb") 168 if adbInPath != None: 169 return adbInPath 170 171 sdkPath = findSDK() 172 if sdkPath != None: 173 adbInSDK = os.path.join(sdkPath, "platform-tools", "adb") 174 if os.path.isfile(adbInSDK): 175 return adbInSDK 176 177 return None 178 179def parseArgs (): 180 defaultADBPath = findADB() 181 defaultBuildRoot = getDefaultBuildRoot() 182 183 parser = argparse.ArgumentParser(os.path.basename(__file__), 184 formatter_class=argparse.ArgumentDefaultsHelpFormatter) 185 parser.add_argument('--build-root', 186 dest='buildRoot', 187 default=defaultBuildRoot, 188 help="Root build directory") 189 parser.add_argument('--adb', 190 dest='adbPath', 191 default=defaultADBPath, 192 help="ADB binary path", 193 required=(True if defaultADBPath == None else False)) 194 parser.add_argument('--target', 195 dest='target', 196 help='Build target', 197 choices=['deqp', 'openglcts'], 198 default='deqp') 199 parser.add_argument('-p', '--parallel', 200 dest='doParallel', 201 action="store_true", 202 help="Install package in parallel") 203 parser.add_argument('-s', '--serial', 204 dest='serial', 205 type=str, 206 nargs='+', 207 help="Install package to device with serial number") 208 parser.add_argument('-a', '--all', 209 dest='all', 210 action="store_true", 211 help="Install to all devices") 212 213 return parser.parse_args() 214 215if __name__ == "__main__": 216 args = parseArgs() 217 packageName = getPackageName(args.target) 218 apkPath = getAPKPath(args.buildRoot, args.target) 219 220 if not os.path.isfile(apkPath): 221 die("%s does not exist" % apkPath) 222 223 if args.all: 224 installToAllDevices(args.doParallel, args.adbPath, packageName, apkPath) 225 else: 226 if args.serial == None: 227 devices = getDevices(args.adbPath) 228 if len(devices) == 0: 229 die('No devices connected') 230 elif len(devices) == 1: 231 installToDevice(devices[0], args.adbPath, packageName, apkPath) 232 else: 233 print("More than one device connected:") 234 for i in range(0, len(devices)): 235 print("%3d: %16s %s" % ((i+1), devices[i].serial, devices[i].model)) 236 237 deviceNdx = int(input("Choose device (1-%d): " % len(devices))) 238 installToDevice(devices[deviceNdx-1], args.adbPath, packageName, apkPath) 239 else: 240 devices = getDevices(args.adbPath) 241 242 devices = [dev for dev in devices if dev.serial in args.serial] 243 devSerials = [dev.serial for dev in devices] 244 notFounds = [serial for serial in args.serial if not serial in devSerials] 245 246 for notFound in notFounds: 247 print("Couldn't find device matching serial '%s'" % notFound) 248 249 installToDevices(devices, args.doParallel, args.adbPath, packageName, apkPath) 250