1#!/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)) 319