blob: acd2093b1fcfb83e17ca776dd0a8197c7c4a16a5 [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001#!/usr/bin/env python
2# Copyright (c) 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Installs deps for using SDK emulator for testing.
7
8The script will download the SDK and system images, if they are not present, and
9install and enable KVM, if virtualization has been enabled in the BIOS.
10"""
11
12
13import logging
14import optparse
15import os
16import re
17import sys
18
19import devil_chromium
20from devil.utils import cmd_helper
21from devil.utils import run_tests_helper
22from pylib import constants
23from pylib import pexpect
24
25# Android API level
26DEFAULT_ANDROID_API_LEVEL = constants.ANDROID_SDK_VERSION
27# Android ABI/Arch
28DEFAULT_ABI = 'x86'
29
30# Default Time out for downloading SDK component
31DOWNLOAD_SYSTEM_IMAGE_TIMEOUT = 30
32DOWNLOAD_SDK_PLATFORM_TIMEOUT = 60
33
34def CheckSDK():
35 """Check if SDK is already installed.
36
37 Returns:
38 True if the emulator SDK directory (src/android_emulator_sdk/) exists.
39 """
40 return os.path.exists(constants.ANDROID_SDK_ROOT)
41
42
43def CheckSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL, google=False):
44 """Check if the "SDK Platform" for the specified API level is installed.
45 This is necessary in order for the emulator to run when the target
46 is specified.
47
48 Args:
49 abi: target abi, x86 or arm
50 api_level: the Android API level to check; defaults to the latest API.
51 google: use Google build system image instead of AOSP build
52
53 Returns:
54 True if the platform is already installed.
55 """
56 android_binary = os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android')
57 if google:
58 pattern = re.compile('id: [0-9]+ or "Google Inc.:Google APIs:%s"' %
59 api_level)
60 else:
61 pattern = re.compile('id: [0-9]+ or "android-%d"' % api_level)
62
63 try:
64 exit_code, stdout = cmd_helper.GetCmdStatusAndOutput(
65 [android_binary, 'list'])
66 if exit_code != 0:
67 raise Exception('\'android list\' command failed')
68 for line in stdout.split('\n'):
69 if pattern.match(line):
70 return True
71 return False
72 except OSError:
73 logging.exception('Unable to execute \'android list\'')
74 return False
75
76
77def CheckSystemImage(abi, api_level=DEFAULT_ANDROID_API_LEVEL, google=False):
78 """Check if Android system images have been installed.
79
80 Args:
81 abi: target abi, x86 or arm
82 api_level: the Android API level to check for; defaults to the latest API.
83 google: use Google build system image instead of AOSP build
84
85 Returns:
86 True if x86 image has been previously downloaded.
87 """
88 api_target = 'android-%d' % api_level
89 system_image_root = os.path.join(constants.ANDROID_SDK_ROOT,
90 'system-images', api_target)
91 if abi == 'x86':
92 if google:
93 return os.path.exists(os.path.join(system_image_root, 'google_apis',
94 'x86'))
95 else:
96 return os.path.exists(os.path.join(system_image_root, 'default', 'x86'))
97 elif abi == 'arm':
98 if google:
99 return os.path.exists(os.path.join(system_image_root, 'google_apis',
100 'armeabi-v7a'))
101 else:
102 return os.path.exists(os.path.join(system_image_root, 'default',
103 'armeabi-v7a'))
104 else:
105 raise Exception("abi option invalid")
106
107def CheckKVM():
108 """Quickly check whether KVM is enabled.
109
110 Returns:
111 True iff /dev/kvm exists (Linux only).
112 """
113 return os.path.exists('/dev/kvm')
114
115def RunKvmOk():
116 """Run kvm-ok as root to check that KVM is properly enabled after installation
117 of the required packages.
118
119 Returns:
120 True iff KVM is enabled (/dev/kvm exists). On failure, returns False
121 but also print detailed information explaining why KVM isn't enabled
122 (e.g. CPU doesn't support it, or BIOS disabled it).
123 """
124 try:
125 # Note: kvm-ok is in /usr/sbin, so always use 'sudo' to run it.
126 return not cmd_helper.RunCmd(['sudo', 'kvm-ok'])
127 except OSError:
128 logging.info('kvm-ok not installed')
129 return False
130
131
132def InstallKVM():
133 """Installs KVM packages."""
134 rc = cmd_helper.RunCmd(['sudo', 'apt-get', 'install', 'kvm'])
135 if rc:
136 logging.critical('ERROR: Did not install KVM. Make sure hardware '
137 'virtualization is enabled in BIOS (i.e. Intel VT-x or '
138 'AMD SVM).')
139 # TODO(navabi): Use modprobe kvm-amd on AMD processors.
140 rc = cmd_helper.RunCmd(['sudo', 'modprobe', 'kvm-intel'])
141 if rc:
142 logging.critical('ERROR: Did not add KVM module to Linux Kernel. Make sure '
143 'hardware virtualization is enabled in BIOS.')
144 # Now check to ensure KVM acceleration can be used.
145 if not RunKvmOk():
146 logging.critical('ERROR: Can not use KVM acceleration. Make sure hardware '
147 'virtualization is enabled in BIOS (i.e. Intel VT-x or '
148 'AMD SVM).')
149
150
151def UpdateSDK(api_level, package_name, package_pattern, timeout):
152 """This function update SDK with a filter index.
153
154 Args:
155 api_level: the Android API level to download for.
156 package_name: logging name of package that is being updated.
157 package_pattern: the pattern to match the filter index from.
158 timeout: the amount of time wait for update command.
159 """
160 android_binary = os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android')
161
162 list_sdk_repo_command = [android_binary, 'list', 'sdk', '--all']
163
164 exit_code, stdout = cmd_helper.GetCmdStatusAndOutput(list_sdk_repo_command)
165
166 if exit_code != 0:
167 raise Exception('\'android list sdk --all\' command return %d' % exit_code)
168
169 for line in stdout.split('\n'):
170 match = package_pattern.match(line)
171 if match:
172 index = match.group(1)
173 logging.info('package %s corresponds to %s with api level %d',
174 index, package_name, api_level)
175 update_command = [android_binary, 'update', 'sdk', '--no-ui', '--all',
176 '--filter', index]
177 update_command_str = ' '.join(update_command)
178 logging.info('running update command: %s', update_command_str)
179 update_process = pexpect.spawn(update_command_str)
180
181 if update_process.expect('Do you accept the license') != 0:
182 raise Exception('License agreement check failed')
183 update_process.sendline('y')
184 if update_process.expect(
185 'Done. 1 package installed.', timeout=timeout) == 0:
186 logging.info('Successfully installed %s for API level %d',
187 package_name, api_level)
188 return
189 else:
190 raise Exception('Failed to install platform update')
191 raise Exception('Could not find android-%d update for the SDK!' % api_level)
192
193def GetSystemImage(abi, api_level=DEFAULT_ANDROID_API_LEVEL, google=False):
194 """Download system image files
195
196 Args:
197 abi: target abi, x86 or arm
198 api_level: the Android API level to download for.
199 google: use Google build system image instead of AOSP build
200 """
201 logging.info('Download x86 system image directory into sdk directory.')
202
203 if abi == 'x86':
204 if google:
205 package_name = 'Google Intel x86 Atom System Image'
206 pattern = re.compile(
207 r'\s*([0-9]+)- Google APIs Intel x86 Atom System Image, Google Inc.'
208 ' API %d.*' % api_level)
209 else:
210 package_name = 'Intel x86 system image'
211 pattern = re.compile(
212 r'\s*([0-9]+)- Intel x86 Atom System Image, Android API %d.*'
213 % api_level)
214 elif abi == 'arm':
215 if google:
216 package_name = 'Google arm system image'
217 pattern = re.compile(
218 r'\s*([0-9]+)- Google APIs ARM EABI v7a System Image, Google Inc. API '
219 '%d.*' % api_level)
220 else:
221 package_name = 'Android arm system image'
222 pattern = re.compile(
223 r'\s*([0-9]+)- ARM EABI v7a System Image, Android API %d.*' % api_level)
224 else:
225 raise Exception('abi option is invalid')
226
227 UpdateSDK(api_level, package_name, pattern, DOWNLOAD_SYSTEM_IMAGE_TIMEOUT)
228
229def GetSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL, google=False):
230 """Update the SDK to include the platform specified.
231
232 Args:
233 api_level: the Android API level to download
234 google: use Google build system image instead of AOSP build
235 """
236 logging.info('Download SDK Platform directory into sdk directory.')
237
238 platform_package_pattern = re.compile(
239 r'\s*([0-9]+)- SDK Platform Android [\.,0-9]+, API %d.*' % api_level)
240
241 UpdateSDK(api_level, 'SDK Platform', platform_package_pattern,
242 DOWNLOAD_SDK_PLATFORM_TIMEOUT)
243
244 if google:
245 google_api_package_pattern = re.compile(
246 r'\s*([0-9]+)- Google APIs, Android API %d.*' % api_level)
247 UpdateSDK(api_level, 'Google APIs', google_api_package_pattern,
248 DOWNLOAD_SDK_PLATFORM_TIMEOUT)
249
250
251def main(argv):
252 opt_parser = optparse.OptionParser(
253 description='Install dependencies for running the Android emulator')
254 opt_parser.add_option('--abi',
255 dest='abi',
256 help='The targeted abi for emulator system image',
257 type='string',
258 default=DEFAULT_ABI)
259 opt_parser.add_option('--api-level',
260 dest='api_level',
261 help=('The API level (e.g., 19 for Android 4.4) to '
262 'ensure is available'),
263 type='int',
264 default=DEFAULT_ANDROID_API_LEVEL)
265 opt_parser.add_option('-v',
266 dest='verbosity',
267 default=1,
268 action='count',
269 help='Verbose level (multiple times for more)')
270 opt_parser.add_option('--google',
271 dest='google',
272 action='store_true',
273 default=False,
274 help='Install Google System Image instead of AOSP')
275
276 options, _ = opt_parser.parse_args(argv[1:])
277
278 run_tests_helper.SetLogLevel(verbose_count=options.verbosity)
279
280 devil_chromium.Initialize()
281
282 # Calls below will download emulator SDK and/or system images only if needed.
283 if CheckSDK():
284 logging.info('android_emulator_sdk/ exists')
285 else:
286 logging.critical('ERROR: Emulator SDK not installed in %s'
287 , constants.ANDROID_SDK_ROOT)
288 return 1
289
290 # Check target. The target has to be installed in order to run the emulator.
291 if CheckSDKPlatform(options.api_level, options.google):
292 logging.info('SDK platform %s %s android-%d already present, skipping.',
293 'Google' if options.google else 'AOSP', options.abi,
294 options.api_level)
295 else:
296 logging.info('SDK platform %s %s android-%d not present, installing.',
297 'Google' if options.google else 'AOSP', options.abi,
298 options.api_level)
299 GetSDKPlatform(options.api_level, options.google)
300
301 # Download the system image needed
302 if CheckSystemImage(options.abi, options.api_level, options.google):
303 logging.info('system image for %s %s android-%d already present, skipping.',
304 'Google' if options.google else 'AOSP', options.abi,
305 options.api_level)
306 else:
307 GetSystemImage(options.abi, options.api_level, options.google)
308
309 # Make sure KVM packages are installed and enabled.
310 if options.abi == 'x86':
311 if CheckKVM():
312 logging.info('KVM already installed and enabled.')
313 else:
314 logging.warning('KVM is not installed or enabled.')
315
316
317if __name__ == '__main__':
318 sys.exit(main(sys.argv))