| #!/usr/bin/env python | 
 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | """Installs deps for using SDK emulator for testing. | 
 |  | 
 | The script will download the SDK and system images, if they are not present, and | 
 | install and enable KVM, if virtualization has been enabled in the BIOS. | 
 | """ | 
 |  | 
 |  | 
 | import logging | 
 | import optparse | 
 | import os | 
 | import re | 
 | import sys | 
 |  | 
 | import devil_chromium | 
 | from devil.utils import cmd_helper | 
 | from devil.utils import run_tests_helper | 
 | from pylib import constants | 
 | from pylib import pexpect | 
 |  | 
 | # Android API level | 
 | DEFAULT_ANDROID_API_LEVEL = constants.ANDROID_SDK_VERSION | 
 | # Android ABI/Arch | 
 | DEFAULT_ABI = 'x86' | 
 |  | 
 | # Default Time out for downloading SDK component | 
 | DOWNLOAD_SYSTEM_IMAGE_TIMEOUT = 30 | 
 | DOWNLOAD_SDK_PLATFORM_TIMEOUT = 60 | 
 |  | 
 | def CheckSDK(): | 
 |   """Check if SDK is already installed. | 
 |  | 
 |   Returns: | 
 |     True if the emulator SDK directory (src/android_emulator_sdk/) exists. | 
 |   """ | 
 |   return os.path.exists(constants.ANDROID_SDK_ROOT) | 
 |  | 
 |  | 
 | def CheckSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL, google=False): | 
 |   """Check if the "SDK Platform" for the specified API level is installed. | 
 |      This is necessary in order for the emulator to run when the target | 
 |      is specified. | 
 |  | 
 |   Args: | 
 |     abi: target abi, x86 or arm | 
 |     api_level: the Android API level to check; defaults to the latest API. | 
 |     google: use Google build system image instead of AOSP build | 
 |  | 
 |   Returns: | 
 |     True if the platform is already installed. | 
 |   """ | 
 |   android_binary = os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android') | 
 |   if google: | 
 |     pattern = re.compile('id: [0-9]+ or "Google Inc.:Google APIs:%s"' % | 
 |                          api_level) | 
 |   else: | 
 |     pattern = re.compile('id: [0-9]+ or "android-%d"' % api_level) | 
 |  | 
 |   try: | 
 |     exit_code, stdout = cmd_helper.GetCmdStatusAndOutput( | 
 |         [android_binary, 'list']) | 
 |     if exit_code != 0: | 
 |       raise Exception('\'android list\' command failed') | 
 |     for line in stdout.split('\n'): | 
 |       if pattern.match(line): | 
 |         return True | 
 |     return False | 
 |   except OSError: | 
 |     logging.exception('Unable to execute \'android list\'') | 
 |     return False | 
 |  | 
 |  | 
 | def CheckSystemImage(abi, api_level=DEFAULT_ANDROID_API_LEVEL, google=False): | 
 |   """Check if Android system images have been installed. | 
 |  | 
 |   Args: | 
 |     abi: target abi, x86 or arm | 
 |     api_level: the Android API level to check for; defaults to the latest API. | 
 |     google: use Google build system image instead of AOSP build | 
 |  | 
 |   Returns: | 
 |     True if x86 image has been previously downloaded. | 
 |   """ | 
 |   api_target = 'android-%d' % api_level | 
 |   system_image_root = os.path.join(constants.ANDROID_SDK_ROOT, | 
 |                                    'system-images', api_target) | 
 |   if abi == 'x86': | 
 |     if google: | 
 |       return os.path.exists(os.path.join(system_image_root, 'google_apis', | 
 |                                          'x86')) | 
 |     else: | 
 |       return os.path.exists(os.path.join(system_image_root, 'default', 'x86')) | 
 |   elif abi == 'arm': | 
 |     if google: | 
 |       return os.path.exists(os.path.join(system_image_root, 'google_apis', | 
 |                                          'armeabi-v7a')) | 
 |     else: | 
 |       return os.path.exists(os.path.join(system_image_root, 'default', | 
 |                                          'armeabi-v7a')) | 
 |   else: | 
 |     raise Exception("abi option invalid") | 
 |  | 
 | def CheckKVM(): | 
 |   """Quickly check whether KVM is enabled. | 
 |  | 
 |   Returns: | 
 |     True iff /dev/kvm exists (Linux only). | 
 |   """ | 
 |   return os.path.exists('/dev/kvm') | 
 |  | 
 | def RunKvmOk(): | 
 |   """Run kvm-ok as root to check that KVM is properly enabled after installation | 
 |      of the required packages. | 
 |  | 
 |   Returns: | 
 |     True iff KVM is enabled (/dev/kvm exists). On failure, returns False | 
 |     but also print detailed information explaining why KVM isn't enabled | 
 |     (e.g. CPU doesn't support it, or BIOS disabled it). | 
 |   """ | 
 |   try: | 
 |     # Note: kvm-ok is in /usr/sbin, so always use 'sudo' to run it. | 
 |     return not cmd_helper.RunCmd(['sudo', 'kvm-ok']) | 
 |   except OSError: | 
 |     logging.info('kvm-ok not installed') | 
 |     return False | 
 |  | 
 |  | 
 | def InstallKVM(): | 
 |   """Installs KVM packages.""" | 
 |   rc = cmd_helper.RunCmd(['sudo', 'apt-get', 'install', 'kvm']) | 
 |   if rc: | 
 |     logging.critical('ERROR: Did not install KVM. Make sure hardware ' | 
 |                      'virtualization is enabled in BIOS (i.e. Intel VT-x or ' | 
 |                      'AMD SVM).') | 
 |   # TODO(navabi): Use modprobe kvm-amd on AMD processors. | 
 |   rc = cmd_helper.RunCmd(['sudo', 'modprobe', 'kvm-intel']) | 
 |   if rc: | 
 |     logging.critical('ERROR: Did not add KVM module to Linux Kernel. Make sure ' | 
 |                      'hardware virtualization is enabled in BIOS.') | 
 |   # Now check to ensure KVM acceleration can be used. | 
 |   if not RunKvmOk(): | 
 |     logging.critical('ERROR: Can not use KVM acceleration. Make sure hardware ' | 
 |                      'virtualization is enabled in BIOS (i.e. Intel VT-x or ' | 
 |                      'AMD SVM).') | 
 |  | 
 |  | 
 | def UpdateSDK(api_level, package_name, package_pattern, timeout): | 
 |   """This function update SDK with a filter index. | 
 |  | 
 |   Args: | 
 |     api_level: the Android API level to download for. | 
 |     package_name: logging name of package that is being updated. | 
 |     package_pattern: the pattern to match the filter index from. | 
 |     timeout: the amount of time wait for update command. | 
 |   """ | 
 |   android_binary = os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android') | 
 |  | 
 |   list_sdk_repo_command = [android_binary, 'list', 'sdk', '--all'] | 
 |  | 
 |   exit_code, stdout = cmd_helper.GetCmdStatusAndOutput(list_sdk_repo_command) | 
 |  | 
 |   if exit_code != 0: | 
 |     raise Exception('\'android list sdk --all\' command return %d' % exit_code) | 
 |  | 
 |   for line in stdout.split('\n'): | 
 |     match = package_pattern.match(line) | 
 |     if match: | 
 |       index = match.group(1) | 
 |       logging.info('package %s corresponds to %s with api level %d', | 
 |                    index, package_name, api_level) | 
 |       update_command = [android_binary, 'update', 'sdk', '--no-ui', '--all', | 
 |                          '--filter', index] | 
 |       update_command_str = ' '.join(update_command) | 
 |       logging.info('running update command: %s', update_command_str) | 
 |       update_process = pexpect.spawn(update_command_str) | 
 |  | 
 |       if update_process.expect('Do you accept the license') != 0: | 
 |         raise Exception('License agreement check failed') | 
 |       update_process.sendline('y') | 
 |       if update_process.expect( | 
 |         'Done. 1 package installed.', timeout=timeout) == 0: | 
 |         logging.info('Successfully installed %s for API level %d', | 
 |                       package_name, api_level) | 
 |         return | 
 |       else: | 
 |         raise Exception('Failed to install platform update') | 
 |   raise Exception('Could not find android-%d update for the SDK!' % api_level) | 
 |  | 
 | def GetSystemImage(abi, api_level=DEFAULT_ANDROID_API_LEVEL, google=False): | 
 |   """Download system image files | 
 |  | 
 |   Args: | 
 |     abi: target abi, x86 or arm | 
 |     api_level: the Android API level to download for. | 
 |     google: use Google build system image instead of AOSP build | 
 |   """ | 
 |   logging.info('Download x86 system image directory into sdk directory.') | 
 |  | 
 |   if abi == 'x86': | 
 |     if google: | 
 |       package_name = 'Google Intel x86 Atom System Image' | 
 |       pattern = re.compile( | 
 |          r'\s*([0-9]+)- Google APIs Intel x86 Atom System Image, Google Inc.' | 
 |         ' API %d.*' % api_level) | 
 |     else: | 
 |       package_name = 'Intel x86 system image' | 
 |       pattern = re.compile( | 
 |         r'\s*([0-9]+)- Intel x86 Atom System Image, Android API %d.*' | 
 |         % api_level) | 
 |   elif abi == 'arm': | 
 |     if google: | 
 |       package_name = 'Google arm system image' | 
 |       pattern = re.compile( | 
 |         r'\s*([0-9]+)- Google APIs ARM EABI v7a System Image, Google Inc. API ' | 
 |         '%d.*' % api_level) | 
 |     else: | 
 |       package_name = 'Android arm system image' | 
 |       pattern = re.compile( | 
 |         r'\s*([0-9]+)- ARM EABI v7a System Image, Android API %d.*' % api_level) | 
 |   else: | 
 |     raise Exception('abi option is invalid') | 
 |  | 
 |   UpdateSDK(api_level, package_name, pattern, DOWNLOAD_SYSTEM_IMAGE_TIMEOUT) | 
 |  | 
 | def GetSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL, google=False): | 
 |   """Update the SDK to include the platform specified. | 
 |  | 
 |   Args: | 
 |     api_level: the Android API level to download | 
 |     google: use Google build system image instead of AOSP build | 
 |   """ | 
 |   logging.info('Download SDK Platform directory into sdk directory.') | 
 |  | 
 |   platform_package_pattern = re.compile( | 
 |       r'\s*([0-9]+)- SDK Platform Android [\.,0-9]+, API %d.*' % api_level) | 
 |  | 
 |   UpdateSDK(api_level, 'SDK Platform', platform_package_pattern, | 
 |             DOWNLOAD_SDK_PLATFORM_TIMEOUT) | 
 |  | 
 |   if google: | 
 |     google_api_package_pattern = re.compile( | 
 |       r'\s*([0-9]+)- Google APIs, Android API %d.*' % api_level) | 
 |     UpdateSDK(api_level, 'Google APIs', google_api_package_pattern, | 
 |               DOWNLOAD_SDK_PLATFORM_TIMEOUT) | 
 |  | 
 |  | 
 | def main(argv): | 
 |   opt_parser = optparse.OptionParser( | 
 |       description='Install dependencies for running the Android emulator') | 
 |   opt_parser.add_option('--abi', | 
 |                         dest='abi', | 
 |                         help='The targeted abi for emulator system image', | 
 |                         type='string', | 
 |                         default=DEFAULT_ABI) | 
 |   opt_parser.add_option('--api-level', | 
 |                         dest='api_level', | 
 |                         help=('The API level (e.g., 19 for Android 4.4) to ' | 
 |                               'ensure is available'), | 
 |                         type='int', | 
 |                         default=DEFAULT_ANDROID_API_LEVEL) | 
 |   opt_parser.add_option('-v', | 
 |                         dest='verbosity', | 
 |                         default=1, | 
 |                         action='count', | 
 |                         help='Verbose level (multiple times for more)') | 
 |   opt_parser.add_option('--google', | 
 |                         dest='google', | 
 |                         action='store_true', | 
 |                         default=False, | 
 |                         help='Install Google System Image instead of AOSP') | 
 |  | 
 |   options, _ = opt_parser.parse_args(argv[1:]) | 
 |  | 
 |   run_tests_helper.SetLogLevel(verbose_count=options.verbosity) | 
 |  | 
 |   devil_chromium.Initialize() | 
 |  | 
 |   # Calls below will download emulator SDK and/or system images only if needed. | 
 |   if CheckSDK(): | 
 |     logging.info('android_emulator_sdk/ exists') | 
 |   else: | 
 |     logging.critical('ERROR: Emulator SDK not installed in %s' | 
 |                      , constants.ANDROID_SDK_ROOT) | 
 |     return 1 | 
 |  | 
 |   # Check target. The target has to be installed in order to run the emulator. | 
 |   if CheckSDKPlatform(options.api_level, options.google): | 
 |     logging.info('SDK platform %s %s android-%d already present, skipping.', | 
 |                  'Google' if options.google else 'AOSP', options.abi, | 
 |                  options.api_level) | 
 |   else: | 
 |     logging.info('SDK platform %s %s android-%d not present, installing.', | 
 |                  'Google' if options.google else 'AOSP', options.abi, | 
 |                  options.api_level) | 
 |     GetSDKPlatform(options.api_level, options.google) | 
 |  | 
 |   # Download the system image needed | 
 |   if CheckSystemImage(options.abi, options.api_level, options.google): | 
 |     logging.info('system image for %s %s android-%d already present, skipping.', | 
 |                  'Google' if options.google else 'AOSP', options.abi, | 
 |                  options.api_level) | 
 |   else: | 
 |     GetSystemImage(options.abi, options.api_level, options.google) | 
 |  | 
 |   # Make sure KVM packages are installed and enabled. | 
 |   if options.abi == 'x86': | 
 |     if CheckKVM(): | 
 |       logging.info('KVM already installed and enabled.') | 
 |     else: | 
 |       logging.warning('KVM is not installed or enabled.') | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main(sys.argv)) |