Home | History | Annotate | Download | only in build
      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