Merge vk-gl-cts/opengl-es-cts-3.2.5 into vk-gl-cts/master
[platform/upstream/VK-GL-CTS.git] / scripts / android / install_apk.py
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
23 import os
24 import re
25 import sys
26 import argparse
27 import threading
28 import subprocess
29
30 from build_apk import findSDK
31 from build_apk import getDefaultBuildRoot
32 from build_apk import getPackageAndLibrariesForTarget
33 from build_apk import getBuildRootRelativeAPKPath
34 from build_apk import parsePackageName
35
36 # Import from <root>/scripts
37 sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
38
39 from build.common import *
40
41 class 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
51 def 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
73 def 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
91 def serialApply (f, argsList):
92         for args in argsList:
93                 f(*args)
94
95 def 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
119 def 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
127 def 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
135 def 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
144 def 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
151 def installToAllDevices (doParallel, adbPath, packageName, apkPath):
152         devices = getDevices(adbPath)
153         installToDevices(devices, doParallel, adbPath, packageName, apkPath)
154
155 def getAPKPath (buildRootPath, target):
156         package = getPackageAndLibrariesForTarget(target)[0]
157         return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package))
158
159 def 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
165 def 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
178 def 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
214 if __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)