1# Copyright 2016 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import re 8import sys 9 10import common 11from autotest_lib.client.common_lib import error 12from autotest_lib.server import utils 13from autotest_lib.server.hosts import adb_host 14from autotest_lib.utils import emulator_manager 15 16OS_TYPE_EMULATED_BRILLO = 'emulated_brillo' 17OS_TYPE_DEFAULT = OS_TYPE_EMULATED_BRILLO 18BOARD_DEFAULT = "brilloemulator_arm" 19EMULATED_BRILLO_ARTIFACT_FORMAT = ( 20 '%(build_target)s-target_files-%(build_id)s.zip') 21EMULATED_BRILLO_DTB_FORMAT = ( 22 '%(build_target)s-dtb-%(build_id)s.zip') 23 24 25class EmulatedADBHost(adb_host.ADBHost): 26 """Run an emulator as an ADB device preserving the API and assumptions of 27 ADBHost. 28 29 Currently supported emulators: 30 * Brillo 31 * brilloemulator_arm 32 """ 33 34 def _initialize(self, *args, **kwargs): 35 """Intialize an emulator so that existing assumptions that the host is 36 always ready ar satisfied. 37 38 @param args: pass through to ADBHost 39 @param kwargs: pass through to ADBHost 40 """ 41 super(EmulatedADBHost, self)._initialize(*args, **kwargs) 42 43 # Verify serial 44 m = re.match('emulator-(\d{4})', self.adb_serial) 45 if not m: 46 raise ValueError('Emulator serial must be in the format ' 47 'emulator-PORT.') 48 self.port = int(m.group(1)) + 1 49 50 # Determine directory for images (needs to be persistent) 51 tmp_dir = self.teststation.get_tmp_dir() 52 self.imagedir = os.path.join(os.path.dirname(tmp_dir), self.adb_serial) 53 self.teststation.run('rm -rf %s' % tmp_dir) 54 self.teststation.run('mkdir -p %s' % self.imagedir) 55 56 # Boot the emulator, if not already booted, since ADBHost assumes the 57 # device is always available 58 self._emulator = emulator_manager.EmulatorManager( 59 self.imagedir, self.port, run=self.teststation.run) 60 self._start_emulator_if_not_started() 61 62 63 def _start_emulator_if_not_started(self): 64 """Boot or reboot the emulator if necessary. 65 66 If the emulator is not started boot the emulator. Otherwise leave it 67 alone. Ensure emulator is running and ready before returning. 68 """ 69 host_os = self.get_os_type() 70 board = self.get_board() 71 72 # Check that images exist in imagedir 73 try: 74 self.teststation.run('test -f %s' % os.path.join(self.imagedir, 75 'system.img')) 76 77 # Use default images 78 except error.GenericHostRunError: 79 self.teststation.run('cp %s/* %s/' % ( 80 os.path.join('/usr/local/emulator_images', host_os, board), 81 self.imagedir 82 )) 83 84 if not self._emulator.find(): 85 self._emulator.start() 86 self.wait_up() 87 self._reset_adbd_connection() 88 89 90 def get_os_type(self): 91 """Determine the OS type from afe_host object or use the default if 92 no os label / no afe_host object. 93 94 @return: os type as str 95 """ 96 info = self.host_info_store.get() 97 return info.os or OS_TYPE_DEFAULT 98 99 100 def get_board(self): 101 """Determine the board from afe_host object or use the default if 102 no board label / no afe_host object. 103 104 @return: board as str 105 """ 106 info = self.host_info_store.get() 107 return info.board or BOARD_DEFAULT 108 109 110 @staticmethod 111 def check_host(host, timeout=10): 112 """No dynamic host checking. Must be explicit. 113 114 @param host: ignored 115 @param timeout: ignored 116 117 @return: False 118 """ 119 return False 120 121 122 def stage_emulator_artifact(self, build_url): 123 """Download required build artifact from the given build_url to a 124 local directory in the machine running the emulator. 125 126 @param build_url: The url to use for downloading Android artifacts. 127 pattern: http://$devserv/static/branch/target/build_id 128 129 @return: Path to the directory contains image files. 130 """ 131 build_info = self.get_build_info_from_build_url(build_url) 132 133 zipped_artifact = EMULATED_BRILLO_ARTIFACT_FORMAT % build_info 134 dtb_artifact = EMULATED_BRILLO_DTB_FORMAT % build_info 135 image_dir = self.teststation.get_tmp_dir() 136 137 try: 138 self.download_file(build_url, zipped_artifact, image_dir, 139 unzip=True) 140 self.download_file(build_url, dtb_artifact, image_dir, 141 unzip=True) 142 return image_dir 143 except: 144 self.teststation.run('rm -rf %s' % image_dir) 145 raise 146 147 148 def setup_brillo_emulator(self, build_url, build_local_path=None): 149 """Install the Brillo DUT. 150 151 Following are the steps used here to provision an android device: 152 1. If build_local_path is not set, download the target_files zip, e.g., 153 brilloemulator_arm-target_files-123456.zip, and unzip it. 154 2. Move the necessary images to a new directory. 155 3. Determine port for ADB from serial. 156 4. Use EmulatorManager to start the emulator. 157 158 @param build_url: The url to use for downloading Android artifacts. 159 pattern: http://$devserver:###/static/$build 160 @param build_local_path: The path to a local folder that contains the 161 image files needed to provision the device. 162 Note that the folder is in the machine running 163 adb command, rather than the drone. 164 165 @raises AndroidInstallError if any error occurs. 166 """ 167 # If the build is not staged in local server yet, clean up the temp 168 # folder used to store image files after the provision is completed. 169 delete_build_folder = bool(not build_local_path) 170 171 try: 172 # Download image files needed for provision to a local directory. 173 if not build_local_path: 174 build_local_path = self.stage_emulator_artifact(build_url) 175 176 # Create directory with required files 177 self.teststation.run('rm -rf %s && mkdir %s' % (self.imagedir, 178 self.imagedir)) 179 self.teststation.run('mv %s %s' % ( 180 os.path.join(build_local_path, 'IMAGES', 'system.img'), 181 os.path.join(self.imagedir, 'system.img') 182 )) 183 self.teststation.run('mv %s %s' % ( 184 os.path.join(build_local_path, 'IMAGES', 'userdata.img'), 185 os.path.join(self.imagedir, 'userdata.img') 186 )) 187 self.teststation.run('mv %s %s' % ( 188 os.path.join(build_local_path, 'BOOT', 'kernel'), 189 os.path.join(self.imagedir, 'kernel') 190 )) 191 self.teststation.run('mv %s/*.dtb %s' % (build_local_path, 192 self.imagedir)) 193 194 # Start the emulator 195 self._emulator.force_stop() 196 self._start_emulator_if_not_started() 197 198 except Exception as e: 199 logging.error('Install Brillo build failed with error: %s', e) 200 # Re-raise the exception with type of AndroidInstallError. 201 raise (adb_host.AndroidInstallError, sys.exc_info()[1], 202 sys.exc_info()[2]) 203 finally: 204 if delete_build_folder: 205 self.teststation.run('rm -rf %s' % build_local_path) 206 logging.info('Successfully installed Android build staged at ' 207 '%s.', build_url) 208 209 210 def machine_install(self, build_url=None, build_local_path=None, wipe=True, 211 flash_all=False, os_type=None): 212 """Install the DUT. 213 214 @param build_url: The url to use for downloading Android artifacts. 215 pattern: http://$devserver:###/static/$build. 216 If build_url is set to None, the code may try 217 _parser.options.image to do the installation. If none 218 of them is set, machine_install will fail. 219 @param build_local_path: The path to a local directory that contains the 220 image files needed to provision the device. 221 @param wipe: No-op 222 @param flash_all: No-op 223 @param os_type: OS to install (overrides label). 224 225 @returns A tuple of (image_name, host_attributes). image_name is the 226 name of image installed, e.g., 227 git_mnc-release/shamu-userdebug/1234 228 host_attributes is a dictionary of (attribute, value), which 229 can be saved to afe_host_attributes table in database. This 230 method returns a dictionary with a single entry of 231 `job_repo_url_[adb_serial]`: devserver_url, where devserver_url 232 is a url to the build staged on devserver. 233 """ 234 os_type = os_type or self.get_os_type() 235 if not build_url and self._parser.options.image: 236 build_url, _ = self.stage_build_for_install( 237 self._parser.options.image, os_type=os_type) 238 if os_type == OS_TYPE_EMULATED_BRILLO: 239 self.setup_brillo_emulator( 240 build_url=build_url, build_local_path=build_local_path) 241 self.ensure_adb_mode() 242 else: 243 raise error.InstallError( 244 'Installation of os type %s is not supported.' % 245 os_type) 246 return (build_url.split('static/')[-1], 247 {self.job_repo_url_attribute: build_url}) 248 249 250 def repair(self): 251 """No-op. No repair procedures for emulated devices. 252 """ 253 pass 254 255 256 def verify_software(self): 257 """Verify commands are available on teststation. 258 259 @return: Bool - teststation has necessary software installed. 260 """ 261 adb = self.teststation.run('which adb') 262 qemu = self.teststation.run('which qemu-system-arm') 263 unzip = self.teststation.run('which unzip') 264 265 return bool(adb and qemu and unzip) 266 267 268 def fastboot_run(self, command, **kwargs): 269 """No-op, emulators do not support fastboot. 270 271 @param command: command to not execute 272 @param kwargs: additional arguments to ignore 273 274 @return: empty CmdResult object 275 """ 276 return utils.CmdResult() 277 278 279 def get_labels(self): 280 """No-op, emulators do not have any detectable labels. 281 282 @return: empty list 283 """ 284 return [] 285 286 287 def get_platform(self): 288 """@return: emulated_adb 289 """ 290 return 'emulated_adb' 291 292