1 # 2 # Copyright (C) 2017 The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 # 16 """Class to flash build artifacts onto devices""" 17 18 import hashlib 19 import logging 20 import os 21 import resource 22 import sys 23 import tempfile 24 import time 25 26 from host_controller import common 27 from vts.utils.python.common import cmd_utils 28 from vts.utils.python.controllers import android_device 29 30 31 class BuildFlasher(object): 32 """Client that manages build flashing. 33 34 Attributes: 35 device: AndroidDevice, the device associated with the client. 36 """ 37 38 def __init__(self, serial="", customflasher_path=""): 39 """Initialize the client. 40 41 If serial not provided, find single device connected. Error if 42 zero or > 1 devices connected. 43 44 Args: 45 serial: optional string, serial number for the device. 46 customflasher_path: optional string, set device to use specified 47 binary to flash a device 48 """ 49 if serial != "": 50 self.device = android_device.AndroidDevice( 51 serial, device_callback_port=-1) 52 else: 53 serials = android_device.list_adb_devices() 54 if len(serials) == 0: 55 serials = android_device.list_fastboot_devices() 56 if len(serials) == 0: 57 raise android_device.AndroidDeviceError( 58 "ADB and fastboot could not find any target devices.") 59 if len(serials) > 1: 60 print( 61 "ADB or fastboot found more than one device: %s" % serials) 62 self.device = android_device.AndroidDevice( 63 serials[0], device_callback_port=-1) 64 if customflasher_path: 65 self.device.SetCustomFlasherPath(customflasher_path) 66 67 def SetSerial(self, serial): 68 """Sets device serial. 69 70 Args: 71 serial: string, a device serial. 72 73 Returns: 74 True if successful; False otherwise. 75 """ 76 if not serial: 77 print("no serial is given to BuildFlasher.SetSerial.") 78 return False 79 80 self.device = android_device.AndroidDevice( 81 serial, device_callback_port=-1) 82 return True 83 84 def FlashGSI(self, system_img, vbmeta_img=None, skip_check=False): 85 """Flash the Generic System Image to the device. 86 87 Args: 88 system_img: string, path to GSI 89 vbmeta_img: string, optional, path to vbmeta image for new devices 90 skip_check: boolean, set True to skip adb-based checks when 91 the DUT is already running its bootloader. 92 """ 93 if not os.path.exists(system_img): 94 raise ValueError("Couldn't find system image at %s" % system_img) 95 if not skip_check: 96 self.device.adb.wait_for_device() 97 if not self.device.isBootloaderMode: 98 self.device.log.info(self.device.adb.reboot_bootloader()) 99 if vbmeta_img is not None: 100 self.device.log.info( 101 self.device.fastboot.flash('vbmeta', vbmeta_img)) 102 self.device.log.info(self.device.fastboot.erase('system')) 103 self.device.log.info(self.device.fastboot.flash('system', system_img)) 104 self.device.log.info(self.device.fastboot.erase('metadata')) 105 self.device.log.info(self.device.fastboot._w()) 106 self.device.log.info(self.device.fastboot.reboot()) 107 108 def Flashall(self, directory): 109 """Flash all images in a directory to the device using flashall. 110 111 Generally the directory is the result of unzipping the .zip from AB. 112 Args: 113 directory: string, path to directory containing images 114 """ 115 # fastboot flashall looks for imgs in $ANDROID_PRODUCT_OUT 116 os.environ['ANDROID_PRODUCT_OUT'] = directory 117 self.device.adb.wait_for_device() 118 if not self.device.isBootloaderMode: 119 self.device.log.info(self.device.adb.reboot_bootloader()) 120 self.device.log.info(self.device.fastboot.flashall()) 121 122 def Flash(self, device_images): 123 """Flash the Generic System Image to the device. 124 125 Args: 126 device_images: dict, where the key is partition name and value is 127 image file path. 128 129 Returns: 130 True if succesful; False otherwise 131 """ 132 if not device_images: 133 logging.warn("Flash skipped because no device image is given.") 134 return False 135 136 if not self.device.isBootloaderMode: 137 self.device.adb.wait_for_device() 138 print("rebooting to bootloader") 139 self.device.log.info(self.device.adb.reboot_bootloader()) 140 141 print("checking to flash bootloader.img and radio.img") 142 for partition in ["bootloader", "radio"]: 143 if partition in device_images: 144 image_path = device_images[partition] 145 self.device.log.info("fastboot flash %s %s", 146 partition, image_path) 147 self.device.log.info( 148 self.device.fastboot.flash(partition, image_path)) 149 self.device.log.info("fastboot reboot_bootloader") 150 self.device.log.info(self.device.fastboot.reboot_bootloader()) 151 152 print("starting to flash vendor and other images...") 153 if common.FULL_ZIPFILE in device_images: 154 print("fastboot update %s --skip-reboot" % 155 (device_images[common.FULL_ZIPFILE])) 156 self.device.log.info( 157 self.device.fastboot.update( 158 device_images[common.FULL_ZIPFILE], 159 "--skip-reboot")) 160 161 for partition, image_path in device_images.iteritems(): 162 if partition in (common.FULL_ZIPFILE, "system", "vbmeta", 163 "bootloader", "radio"): 164 continue 165 if not image_path: 166 self.device.log.warning("%s image is empty", partition) 167 continue 168 self.device.log.info("fastboot flash %s %s", partition, image_path) 169 self.device.log.info( 170 self.device.fastboot.flash(partition, image_path)) 171 172 print("starting to flash system and other images...") 173 if "system" in device_images and device_images["system"]: 174 system_img = device_images["system"] 175 vbmeta_img = device_images["vbmeta"] if ( 176 "vbmeta" in device_images 177 and device_images["vbmeta"]) else None 178 self.FlashGSI(system_img, vbmeta_img, skip_check=True) 179 else: 180 self.device.log.info(self.device.fastboot.reboot()) 181 return True 182 183 def FlashImage(self, device_images, reboot=False): 184 """Flash specified image(s) to the device. 185 186 Args: 187 device_images: dict, where the key is partition name and value is 188 image file path. 189 reboot: boolean, true to reboot the device. 190 191 Returns: 192 True if successful, False otherwise 193 """ 194 if not device_images: 195 logging.warn("Flash skipped because no device image is given.") 196 return False 197 198 if not self.device.isBootloaderMode: 199 self.device.adb.wait_for_device() 200 self.device.log.info(self.device.adb.reboot_bootloader()) 201 202 for partition, image_path in device_images.iteritems(): 203 if partition.endswith(".img"): 204 partition = partition[:-4] 205 self.device.log.info( 206 self.device.fastboot.flash(partition, image_path)) 207 if reboot: 208 self.device.log.info(self.device.fastboot.reboot()) 209 return True 210 211 def WaitForDevice(self, timeout_secs=600): 212 """Waits for the device to boot completely. 213 214 Args: 215 timeout_secs: integer, the maximum timeout value for this 216 operation (unit: seconds). 217 218 Returns: 219 True if device is booted successfully; False otherwise. 220 """ 221 return self.device.waitForBootCompletion(timeout=timeout_secs) 222 223 def FlashUsingCustomBinary(self, 224 device_images, 225 reboot_mode, 226 flasher_args, 227 timeout_secs_for_reboot=900): 228 """Flash the customized image to the device. 229 230 Args: 231 device_images: dict, where the key is partition name and value is 232 image file path. 233 reboot_mode: string, decides which mode device will reboot into. 234 ("bootloader"/"download"). 235 flasher_args: list of strings, arguments that will be passed to the 236 flash binary. 237 timeout_secs_for_reboot: integer, the maximum timeout value for 238 reboot to flash-able mode(unit: seconds). 239 240 Returns: 241 True if successful; False otherwise. 242 """ 243 if not device_images: 244 logging.warn("Flash skipped because no device image is given.") 245 return False 246 247 if not flasher_args: 248 logging.error("No arguments.") 249 return False 250 251 if not self.device.isBootloaderMode: 252 self.device.adb.wait_for_device() 253 print("rebooting to %s mode" % reboot_mode) 254 self.device.log.info(self.device.adb.reboot(reboot_mode)) 255 256 start = time.time() 257 while not self.device.customflasher._l(): 258 if time.time() - start >= timeout_secs_for_reboot: 259 logging.error( 260 "Timeout while waiting for %s mode boot completion." % 261 reboot_mode) 262 return False 263 time.sleep(1) 264 265 flasher_output = self.device.customflasher.ExecCustomFlasherCmd( 266 flasher_args[0], 267 " ".join(flasher_args[1:] + [device_images["img"]])) 268 self.device.log.info(flasher_output) 269 270 return True 271 272 def RepackageArtifacts(self, device_images, repackage_form): 273 """Repackage artifacts into a given format. 274 275 Once repackaged, device_images becomes 276 {"img": "path_to_repackaged_image"} 277 278 Args: 279 device_images: dict, where the key is partition name and value is 280 image file path. 281 repackage_form: string, format to repackage. 282 283 Returns: 284 True if succesful; False otherwise. 285 """ 286 if not device_images: 287 logging.warn("Repackage skipped because no device image is given.") 288 return False 289 290 if repackage_form == "tar.md5": 291 tmp_file_name = next(tempfile._get_candidate_names()) + ".tar" 292 tmp_dir_path = os.path.dirname( 293 device_images[device_images.keys()[0]]) 294 for img in device_images: 295 if os.path.dirname(device_images[img]) != tmp_dir_path: 296 os.rename(device_images[img], 297 os.path.join(tmp_dir_path, img)) 298 device_images[img] = os.path.join(tmp_dir_path, img) 299 300 current_dir = os.getcwd() 301 os.chdir(tmp_dir_path) 302 303 if sys.platform == "linux2": 304 tar_cmd = "tar -cf %s %s" % (tmp_file_name, ' '.join( 305 (device_images.keys()))) 306 else: 307 logging.error("Unsupported OS for the given repackage form.") 308 return False 309 logging.info(tar_cmd) 310 std_out, std_err, err_code = cmd_utils.ExecuteOneShellCommand( 311 tar_cmd) 312 if err_code: 313 logging.error(std_err) 314 return False 315 316 hash_md5 = hashlib.md5() 317 try: 318 with open(tmp_file_name, "rb") as file: 319 data_chunk = 0 320 chunk_size = resource.getpagesize() 321 while data_chunk != b'': 322 data_chunk = file.read(chunk_size) 323 hash_md5.update(data_chunk) 324 hash_ret = hash_md5.hexdigest() 325 with open(tmp_file_name, "a") as file: 326 file.write("%s %s" % (hash_ret, tmp_file_name)) 327 except IOError as e: 328 logging.error(e.strerror) 329 return False 330 331 device_images.clear() 332 device_images["img"] = os.path.join(tmp_dir_path, tmp_file_name) 333 334 os.chdir(current_dir) 335 else: 336 logging.error( 337 "Please specify correct repackage form: --repackage=%s" % 338 repackage_form) 339 return False 340 341 return True 342