blob: 82d1c751373aaa1642890f110ed9179cfbedd19e [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001#!/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 shutil
18import sys
19
20from pylib import cmd_helper
21from pylib import constants
22from pylib import pexpect
23from pylib.utils import run_tests_helper
24
25# Android API level
26DEFAULT_ANDROID_API_LEVEL = constants.ANDROID_SDK_VERSION
27
28# From the Android Developer's website.
29# Keep this up to date; the user can install older API levels as necessary.
30SDK_BASE_URL = 'http://dl.google.com/android/adt'
31SDK_ZIP = 'adt-bundle-linux-x86_64-20131030.zip'
32
James Robinsonc4c1c592014-11-21 18:27:04 -080033# pylint: disable=line-too-long
James Robinson646469d2014-10-03 15:33:28 -070034# Android x86 system image from the Intel website:
35# http://software.intel.com/en-us/articles/intel-eula-x86-android-4-2-jelly-bean-bin
36# These don't exist prior to Android-15.
37# As of 08 Nov 2013, Android-19 is not yet available either.
38X86_IMG_URLS = {
39 15: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-15_r01.zip',
40 16: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-16_r01.zip',
41 17: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-17_r01.zip',
42 18: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-18_r01.zip',
43 19: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-19_r01.zip'}
James Robinsonc4c1c592014-11-21 18:27:04 -080044#pylint: enable=line-too-long
James Robinson646469d2014-10-03 15:33:28 -070045
46def CheckSDK():
47 """Check if SDK is already installed.
48
49 Returns:
50 True if the emulator SDK directory (src/android_emulator_sdk/) exists.
51 """
52 return os.path.exists(constants.EMULATOR_SDK_ROOT)
53
54
55def CheckSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL):
56 """Check if the "SDK Platform" for the specified API level is installed.
57 This is necessary in order for the emulator to run when the target
58 is specified.
59
60 Args:
61 api_level: the Android API level to check; defaults to the latest API.
62
63 Returns:
64 True if the platform is already installed.
65 """
66 android_binary = os.path.join(constants.EMULATOR_SDK_ROOT,
67 'sdk', 'tools', 'android')
68 pattern = re.compile('id: [0-9]+ or "android-%d"' % api_level)
69 try:
70 exit_code, stdout = cmd_helper.GetCmdStatusAndOutput(
71 [android_binary, 'list'])
72 if exit_code != 0:
73 raise Exception('\'android list\' command failed')
74 for line in stdout.split('\n'):
75 if pattern.match(line):
76 return True
77 return False
78 except OSError:
79 logging.exception('Unable to execute \'android list\'')
80 return False
81
82
83def CheckX86Image(api_level=DEFAULT_ANDROID_API_LEVEL):
84 """Check if Android system images have been installed.
85
86 Args:
87 api_level: the Android API level to check for; defaults to the latest API.
88
89 Returns:
90 True if sdk/system-images/android-<api_level>/x86 exists inside
91 EMULATOR_SDK_ROOT.
92 """
93 api_target = 'android-%d' % api_level
94 return os.path.exists(os.path.join(constants.EMULATOR_SDK_ROOT,
95 'sdk', 'system-images',
96 api_target, 'x86'))
97
98
99def CheckKVM():
100 """Quickly check whether KVM is enabled.
101
102 Returns:
103 True iff /dev/kvm exists (Linux only).
104 """
105 return os.path.exists('/dev/kvm')
106
107
108def RunKvmOk():
109 """Run kvm-ok as root to check that KVM is properly enabled after installation
110 of the required packages.
111
112 Returns:
113 True iff KVM is enabled (/dev/kvm exists). On failure, returns False
114 but also print detailed information explaining why KVM isn't enabled
115 (e.g. CPU doesn't support it, or BIOS disabled it).
116 """
117 try:
118 # Note: kvm-ok is in /usr/sbin, so always use 'sudo' to run it.
119 return not cmd_helper.RunCmd(['sudo', 'kvm-ok'])
120 except OSError:
121 logging.info('kvm-ok not installed')
122 return False
123
124
125def GetSDK():
126 """Download the SDK and unzip it into EMULATOR_SDK_ROOT."""
127 logging.info('Download Android SDK.')
128 sdk_url = '%s/%s' % (SDK_BASE_URL, SDK_ZIP)
129 try:
130 cmd_helper.RunCmd(['curl', '-o', '/tmp/sdk.zip', sdk_url])
131 print 'curled unzipping...'
132 rc = cmd_helper.RunCmd(['unzip', '-o', '/tmp/sdk.zip', '-d', '/tmp/'])
133 if rc:
134 raise Exception('ERROR: could not download/unzip Android SDK.')
135 # Get the name of the sub-directory that everything will be extracted to.
136 dirname, _ = os.path.splitext(SDK_ZIP)
137 zip_dir = '/tmp/%s' % dirname
138 # Move the extracted directory to EMULATOR_SDK_ROOT
139 shutil.move(zip_dir, constants.EMULATOR_SDK_ROOT)
140 finally:
141 os.unlink('/tmp/sdk.zip')
142
143
144def InstallKVM():
145 """Installs KVM packages."""
146 rc = cmd_helper.RunCmd(['sudo', 'apt-get', 'install', 'kvm'])
147 if rc:
148 logging.critical('ERROR: Did not install KVM. Make sure hardware '
149 'virtualization is enabled in BIOS (i.e. Intel VT-x or '
150 'AMD SVM).')
151 # TODO(navabi): Use modprobe kvm-amd on AMD processors.
152 rc = cmd_helper.RunCmd(['sudo', 'modprobe', 'kvm-intel'])
153 if rc:
154 logging.critical('ERROR: Did not add KVM module to Linux Kernel. Make sure '
155 'hardware virtualization is enabled in BIOS.')
156 # Now check to ensure KVM acceleration can be used.
157 if not RunKvmOk():
158 logging.critical('ERROR: Can not use KVM acceleration. Make sure hardware '
159 'virtualization is enabled in BIOS (i.e. Intel VT-x or '
160 'AMD SVM).')
161
162
163def GetX86Image(api_level=DEFAULT_ANDROID_API_LEVEL):
164 """Download x86 system image from Intel's website.
165
166 Args:
167 api_level: the Android API level to download for.
168 """
169 logging.info('Download x86 system image directory into sdk directory.')
170 # TODO(andrewhayden): Use python tempfile lib instead
171 temp_file = '/tmp/x86_img_android-%d.zip' % api_level
172 if api_level not in X86_IMG_URLS:
173 raise Exception('ERROR: no URL known for x86 image for android-%s' %
174 api_level)
175 try:
176 cmd_helper.RunCmd(['curl', '-o', temp_file, X86_IMG_URLS[api_level]])
177 rc = cmd_helper.RunCmd(['unzip', '-o', temp_file, '-d', '/tmp/'])
178 if rc:
179 raise Exception('ERROR: Could not download/unzip image zip.')
180 api_target = 'android-%d' % api_level
181 sys_imgs = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk',
182 'system-images', api_target, 'x86')
183 logging.info('Deploying system image to %s' % sys_imgs)
184 shutil.move('/tmp/x86', sys_imgs)
185 finally:
186 os.unlink(temp_file)
187
188
189def GetSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL):
190 """Update the SDK to include the platform specified.
191
192 Args:
193 api_level: the Android API level to download
194 """
195 android_binary = os.path.join(constants.EMULATOR_SDK_ROOT,
196 'sdk', 'tools', 'android')
James Robinsonc4c1c592014-11-21 18:27:04 -0800197 pattern = re.compile(
198 r'\s*([0-9]+)- SDK Platform Android [\.,0-9]+, API %d.*' % api_level)
James Robinson646469d2014-10-03 15:33:28 -0700199 # Example:
200 # 2- SDK Platform Android 4.3, API 18, revision 2
201 exit_code, stdout = cmd_helper.GetCmdStatusAndOutput(
202 [android_binary, 'list', 'sdk'])
203 if exit_code != 0:
204 raise Exception('\'android list sdk\' command return %d' % exit_code)
205 for line in stdout.split('\n'):
206 match = pattern.match(line)
207 if match:
208 index = match.group(1)
James Robinsonc4c1c592014-11-21 18:27:04 -0800209 print 'package %s corresponds to platform level %d' % (index, api_level)
James Robinson646469d2014-10-03 15:33:28 -0700210 # update sdk --no-ui --filter $INDEX
211 update_command = [android_binary,
212 'update', 'sdk', '--no-ui', '--filter', index]
213 update_command_str = ' '.join(update_command)
214 logging.info('running update command: %s' % update_command_str)
215 update_process = pexpect.spawn(update_command_str)
216 # TODO(andrewhayden): Do we need to bug the user about this?
217 if update_process.expect('Do you accept the license') != 0:
218 raise Exception('License agreement check failed')
219 update_process.sendline('y')
220 if update_process.expect('Done. 1 package installed.') == 0:
James Robinsonc4c1c592014-11-21 18:27:04 -0800221 print 'Successfully installed platform for API level %d' % api_level
James Robinson646469d2014-10-03 15:33:28 -0700222 return
223 else:
224 raise Exception('Failed to install platform update')
225 raise Exception('Could not find android-%d update for the SDK!' % api_level)
226
227
228def main(argv):
229 opt_parser = optparse.OptionParser(
230 description='Install dependencies for running the Android emulator')
231 opt_parser.add_option('--api-level', dest='api_level',
232 help='The API level (e.g., 19 for Android 4.4) to ensure is available',
233 type='int', default=DEFAULT_ANDROID_API_LEVEL)
234 opt_parser.add_option('-v', dest='verbose', action='store_true',
235 help='enable verbose logging')
236 options, _ = opt_parser.parse_args(argv[1:])
237
238 # run_tests_helper will set logging to INFO or DEBUG
239 # We achieve verbose output by configuring it with 2 (==DEBUG)
240 verbosity = 1
James Robinsonc4c1c592014-11-21 18:27:04 -0800241 if options.verbose:
James Robinson646469d2014-10-03 15:33:28 -0700242 verbosity = 2
243 logging.basicConfig(level=logging.INFO,
244 format='# %(asctime)-15s: %(message)s')
245 run_tests_helper.SetLogLevel(verbose_count=verbosity)
246
247 # Calls below will download emulator SDK and/or system images only if needed.
248 if CheckSDK():
249 logging.info('android_emulator_sdk/ already exists, skipping download.')
250 else:
251 GetSDK()
252
253 # Check target. The target has to be installed in order to run the emulator.
254 if CheckSDKPlatform(options.api_level):
255 logging.info('SDK platform android-%d already present, skipping.' %
256 options.api_level)
257 else:
258 logging.info('SDK platform android-%d not present, installing.' %
259 options.api_level)
260 GetSDKPlatform(options.api_level)
261
262 # Download the x86 system image only if needed.
263 if CheckX86Image(options.api_level):
264 logging.info('x86 image for android-%d already present, skipping.' %
265 options.api_level)
266 else:
267 GetX86Image(options.api_level)
268
269 # Make sure KVM packages are installed and enabled.
270 if CheckKVM():
271 logging.info('KVM already installed and enabled.')
272 else:
273 InstallKVM()
274
275
276if __name__ == '__main__':
277 sys.exit(main(sys.argv))