• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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