Home | History | Annotate | Download | only in releasetools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (C) 2014 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """
     18 Given a target-files zipfile that does not contain images (ie, does
     19 not have an IMAGES/ top-level subdirectory), produce the images and
     20 add them to the zipfile.
     21 
     22 Usage:  add_img_to_target_files [flag] target_files
     23 
     24   -a  (--add_missing)
     25       Build and add missing images to "IMAGES/". If this option is
     26       not specified, this script will simply exit when "IMAGES/"
     27       directory exists in the target file.
     28 
     29   -r  (--rebuild_recovery)
     30       Rebuild the recovery patch and write it to the system image. Only
     31       meaningful when system image needs to be rebuilt.
     32 
     33   --replace_verity_private_key
     34       Replace the private key used for verity signing. (same as the option
     35       in sign_target_files_apks)
     36 
     37   --replace_verity_public_key
     38        Replace the certificate (public key) used for verity verification. (same
     39        as the option in sign_target_files_apks)
     40 
     41   --is_signing
     42       Skip building & adding the images for "userdata" and "cache" if we
     43       are signing the target files.
     44 """
     45 
     46 from __future__ import print_function
     47 
     48 import sys
     49 
     50 if sys.hexversion < 0x02070000:
     51   print("Python 2.7 or newer is required.", file=sys.stderr)
     52   sys.exit(1)
     53 
     54 import datetime
     55 import errno
     56 import hashlib
     57 import os
     58 import shlex
     59 import shutil
     60 import subprocess
     61 import tempfile
     62 import uuid
     63 import zipfile
     64 
     65 import build_image
     66 import common
     67 import rangelib
     68 import sparse_img
     69 
     70 OPTIONS = common.OPTIONS
     71 
     72 OPTIONS.add_missing = False
     73 OPTIONS.rebuild_recovery = False
     74 OPTIONS.replace_updated_files_list = []
     75 OPTIONS.replace_verity_public_key = False
     76 OPTIONS.replace_verity_private_key = False
     77 OPTIONS.is_signing = False
     78 
     79 
     80 class OutputFile(object):
     81   def __init__(self, output_zip, input_dir, prefix, name):
     82     self._output_zip = output_zip
     83     self.input_name = os.path.join(input_dir, prefix, name)
     84 
     85     if self._output_zip:
     86       self._zip_name = os.path.join(prefix, name)
     87 
     88       root, suffix = os.path.splitext(name)
     89       self.name = common.MakeTempFile(prefix=root + '-', suffix=suffix)
     90     else:
     91       self.name = self.input_name
     92 
     93   def Write(self):
     94     if self._output_zip:
     95       common.ZipWrite(self._output_zip, self.name, self._zip_name)
     96 
     97 
     98 def GetCareMap(which, imgname):
     99   """Generate care_map of system (or vendor) partition"""
    100 
    101   assert which in ("system", "vendor")
    102 
    103   simg = sparse_img.SparseImage(imgname)
    104   care_map_list = [which]
    105 
    106   care_map_ranges = simg.care_map
    107   key = which + "_adjusted_partition_size"
    108   adjusted_blocks = OPTIONS.info_dict.get(key)
    109   if adjusted_blocks:
    110     assert adjusted_blocks > 0, "blocks should be positive for " + which
    111     care_map_ranges = care_map_ranges.intersect(rangelib.RangeSet(
    112         "0-%d" % (adjusted_blocks,)))
    113 
    114   care_map_list.append(care_map_ranges.to_string_raw())
    115   return care_map_list
    116 
    117 
    118 def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
    119   """Turn the contents of SYSTEM into a system image and store it in
    120   output_zip. Returns the name of the system image file."""
    121 
    122   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.img")
    123   if os.path.exists(img.input_name):
    124     print("system.img already exists in %s, no need to rebuild..." % (prefix,))
    125     return img.input_name
    126 
    127   def output_sink(fn, data):
    128     ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
    129     ofile.write(data)
    130     ofile.close()
    131 
    132     arc_name = "SYSTEM/" + fn
    133     if arc_name in output_zip.namelist():
    134       OPTIONS.replace_updated_files_list.append(arc_name)
    135     else:
    136       common.ZipWrite(output_zip, ofile.name, arc_name)
    137 
    138   if OPTIONS.rebuild_recovery:
    139     print("Building new recovery patch")
    140     common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
    141                              boot_img, info_dict=OPTIONS.info_dict)
    142 
    143   block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.map")
    144   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
    145               block_list=block_list)
    146 
    147   return img.name
    148 
    149 
    150 def AddSystemOther(output_zip, prefix="IMAGES/"):
    151   """Turn the contents of SYSTEM_OTHER into a system_other image
    152   and store it in output_zip."""
    153 
    154   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system_other.img")
    155   if os.path.exists(img.input_name):
    156     print("system_other.img already exists in %s, no need to rebuild..." % (
    157         prefix,))
    158     return
    159 
    160   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
    161 
    162 
    163 def AddVendor(output_zip, prefix="IMAGES/"):
    164   """Turn the contents of VENDOR into a vendor image and store in it
    165   output_zip."""
    166 
    167   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.img")
    168   if os.path.exists(img.input_name):
    169     print("vendor.img already exists in %s, no need to rebuild..." % (prefix,))
    170     return img.input_name
    171 
    172   block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.map")
    173   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
    174               block_list=block_list)
    175   return img.name
    176 
    177 
    178 def AddDtbo(output_zip, prefix="IMAGES/"):
    179   """Adds the DTBO image.
    180 
    181   Uses the image under prefix if it already exists. Otherwise looks for the
    182   image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
    183   """
    184 
    185   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "dtbo.img")
    186   if os.path.exists(img.input_name):
    187     print("dtbo.img already exists in %s, no need to rebuild..." % (prefix,))
    188     return img.input_name
    189 
    190   dtbo_prebuilt_path = os.path.join(
    191       OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
    192   assert os.path.exists(dtbo_prebuilt_path)
    193   shutil.copy(dtbo_prebuilt_path, img.name)
    194 
    195   # AVB-sign the image as needed.
    196   if OPTIONS.info_dict.get("avb_enable") == "true":
    197     avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
    198     part_size = OPTIONS.info_dict["dtbo_size"]
    199     # The AVB hash footer will be replaced if already present.
    200     cmd = [avbtool, "add_hash_footer", "--image", img.name,
    201            "--partition_size", str(part_size), "--partition_name", "dtbo"]
    202     common.AppendAVBSigningArgs(cmd, "dtbo")
    203     args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
    204     if args and args.strip():
    205       cmd.extend(shlex.split(args))
    206     p = common.Run(cmd, stdout=subprocess.PIPE)
    207     p.communicate()
    208     assert p.returncode == 0, \
    209         "avbtool add_hash_footer of %s failed" % (img.name,)
    210 
    211   img.Write()
    212   return img.name
    213 
    214 
    215 def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
    216   print("creating " + what + ".img...")
    217 
    218   # The name of the directory it is making an image out of matters to
    219   # mkyaffs2image.  It wants "system" but we have a directory named
    220   # "SYSTEM", so create a symlink.
    221   temp_dir = tempfile.mkdtemp()
    222   OPTIONS.tempfiles.append(temp_dir)
    223   try:
    224     os.symlink(os.path.join(input_dir, what.upper()),
    225                os.path.join(temp_dir, what))
    226   except OSError as e:
    227     # bogus error on my mac version?
    228     #   File "./build/tools/releasetools/img_from_target_files"
    229     #     os.path.join(OPTIONS.input_tmp, "system"))
    230     # OSError: [Errno 17] File exists
    231     if e.errno == errno.EEXIST:
    232       pass
    233 
    234   image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
    235   fstab = info_dict["fstab"]
    236   mount_point = "/" + what
    237   if fstab and mount_point in fstab:
    238     image_props["fs_type"] = fstab[mount_point].fs_type
    239 
    240   # Use a fixed timestamp (01/01/2009) when packaging the image.
    241   # Bug: 24377993
    242   epoch = datetime.datetime.fromtimestamp(0)
    243   timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
    244   image_props["timestamp"] = int(timestamp)
    245 
    246   if what == "system":
    247     fs_config_prefix = ""
    248   else:
    249     fs_config_prefix = what + "_"
    250 
    251   fs_config = os.path.join(
    252       input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
    253   if not os.path.exists(fs_config):
    254     fs_config = None
    255 
    256   # Override values loaded from info_dict.
    257   if fs_config:
    258     image_props["fs_config"] = fs_config
    259   if block_list:
    260     image_props["block_list"] = block_list.name
    261 
    262   # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
    263   # build fingerprint).
    264   uuid_seed = what + "-"
    265   if "build.prop" in info_dict:
    266     build_prop = info_dict["build.prop"]
    267     if "ro.build.fingerprint" in build_prop:
    268       uuid_seed += build_prop["ro.build.fingerprint"]
    269     elif "ro.build.thumbprint" in build_prop:
    270       uuid_seed += build_prop["ro.build.thumbprint"]
    271   image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
    272   hash_seed = "hash_seed-" + uuid_seed
    273   image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
    274 
    275   succ = build_image.BuildImage(os.path.join(temp_dir, what),
    276                                 image_props, output_file.name)
    277   assert succ, "build " + what + ".img image failed"
    278 
    279   output_file.Write()
    280   if block_list:
    281     block_list.Write()
    282 
    283   # Set the 'adjusted_partition_size' that excludes the verity blocks of the
    284   # given image. When avb is enabled, this size is the max image size returned
    285   # by the avb tool.
    286   is_verity_partition = "verity_block_device" in image_props
    287   verity_supported = (image_props.get("verity") == "true" or
    288                       image_props.get("avb_enable") == "true")
    289   is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
    290   if verity_supported and (is_verity_partition or is_avb_enable):
    291     adjusted_blocks_value = image_props.get("partition_size")
    292     if adjusted_blocks_value:
    293       adjusted_blocks_key = what + "_adjusted_partition_size"
    294       info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1
    295 
    296 
    297 def AddUserdata(output_zip, prefix="IMAGES/"):
    298   """Create a userdata image and store it in output_zip.
    299 
    300   In most case we just create and store an empty userdata.img;
    301   But the invoker can also request to create userdata.img with real
    302   data from the target files, by setting "userdata_img_with_data=true"
    303   in OPTIONS.info_dict.
    304   """
    305 
    306   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "userdata.img")
    307   if os.path.exists(img.input_name):
    308     print("userdata.img already exists in %s, no need to rebuild..." % (
    309         prefix,))
    310     return
    311 
    312   # Skip userdata.img if no size.
    313   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
    314   if not image_props.get("partition_size"):
    315     return
    316 
    317   print("creating userdata.img...")
    318 
    319   # Use a fixed timestamp (01/01/2009) when packaging the image.
    320   # Bug: 24377993
    321   epoch = datetime.datetime.fromtimestamp(0)
    322   timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
    323   image_props["timestamp"] = int(timestamp)
    324 
    325   # The name of the directory it is making an image out of matters to
    326   # mkyaffs2image.  So we create a temp dir, and within it we create an
    327   # empty dir named "data", or a symlink to the DATA dir,
    328   # and build the image from that.
    329   temp_dir = tempfile.mkdtemp()
    330   OPTIONS.tempfiles.append(temp_dir)
    331   user_dir = os.path.join(temp_dir, "data")
    332   empty = (OPTIONS.info_dict.get("userdata_img_with_data") != "true")
    333   if empty:
    334     # Create an empty dir.
    335     os.mkdir(user_dir)
    336   else:
    337     # Symlink to the DATA dir.
    338     os.symlink(os.path.join(OPTIONS.input_tmp, "DATA"),
    339                user_dir)
    340 
    341   fstab = OPTIONS.info_dict["fstab"]
    342   if fstab:
    343     image_props["fs_type"] = fstab["/data"].fs_type
    344   succ = build_image.BuildImage(user_dir, image_props, img.name)
    345   assert succ, "build userdata.img image failed"
    346 
    347   common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
    348   img.Write()
    349 
    350 
    351 def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir):
    352   if not img_path:
    353     return
    354 
    355   # Check if chain partition is used.
    356   key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
    357   if key_path:
    358     # extract public key in AVB format to be included in vbmeta.img
    359     avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
    360     public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition)
    361     p = common.Run([avbtool, "extract_public_key", "--key", key_path,
    362                     "--output", public_key_path],
    363                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    364     p.communicate()
    365     assert p.returncode == 0, \
    366         "avbtool extract_public_key fail for partition: %r" % partition
    367 
    368     rollback_index_location = OPTIONS.info_dict[
    369         "avb_" + partition + "_rollback_index_location"]
    370     cmd.extend(["--chain_partition", "%s:%s:%s" % (
    371         partition, rollback_index_location, public_key_path)])
    372   else:
    373     cmd.extend(["--include_descriptors_from_image", img_path])
    374 
    375 
    376 def AddVBMeta(output_zip, boot_img_path, system_img_path, vendor_img_path,
    377               dtbo_img_path, prefix="IMAGES/"):
    378   """Create a VBMeta image and store it in output_zip."""
    379   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img")
    380   avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
    381   cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
    382   common.AppendAVBSigningArgs(cmd, "vbmeta")
    383 
    384   public_key_dir = tempfile.mkdtemp(prefix="avbpubkey-")
    385   OPTIONS.tempfiles.append(public_key_dir)
    386 
    387   AppendVBMetaArgsForPartition(cmd, "boot", boot_img_path, public_key_dir)
    388   AppendVBMetaArgsForPartition(cmd, "system", system_img_path, public_key_dir)
    389   AppendVBMetaArgsForPartition(cmd, "vendor", vendor_img_path, public_key_dir)
    390   AppendVBMetaArgsForPartition(cmd, "dtbo", dtbo_img_path, public_key_dir)
    391 
    392   args = OPTIONS.info_dict.get("avb_vbmeta_args")
    393   if args and args.strip():
    394     split_args = shlex.split(args)
    395     for index, arg in enumerate(split_args[:-1]):
    396       # Sanity check that the image file exists. Some images might be defined
    397       # as a path relative to source tree, which may not be available at the
    398       # same location when running this script (we have the input target_files
    399       # zip only). For such cases, we additionally scan other locations (e.g.
    400       # IMAGES/, RADIO/, etc) before bailing out.
    401       if arg == '--include_descriptors_from_image':
    402         image_path = split_args[index + 1]
    403         if os.path.exists(image_path):
    404           continue
    405         found = False
    406         for dir in ['IMAGES', 'RADIO', 'VENDOR_IMAGES', 'PREBUILT_IMAGES']:
    407           alt_path = os.path.join(
    408               OPTIONS.input_tmp, dir, os.path.basename(image_path))
    409           if os.path.exists(alt_path):
    410             split_args[index + 1] = alt_path
    411             found = True
    412             break
    413         assert found, 'failed to find %s' % (image_path,)
    414     cmd.extend(split_args)
    415 
    416   p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    417   p.communicate()
    418   assert p.returncode == 0, "avbtool make_vbmeta_image failed"
    419   img.Write()
    420 
    421 
    422 def AddPartitionTable(output_zip, prefix="IMAGES/"):
    423   """Create a partition table image and store it in output_zip."""
    424 
    425   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.img")
    426   bpt = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.bpt")
    427 
    428   # use BPTTOOL from environ, or "bpttool" if empty or not set.
    429   bpttool = os.getenv("BPTTOOL") or "bpttool"
    430   cmd = [bpttool, "make_table", "--output_json", bpt.name,
    431          "--output_gpt", img.name]
    432   input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
    433   input_files = input_files_str.split(" ")
    434   for i in input_files:
    435     cmd.extend(["--input", i])
    436   disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
    437   if disk_size:
    438     cmd.extend(["--disk_size", disk_size])
    439   args = OPTIONS.info_dict.get("board_bpt_make_table_args")
    440   if args:
    441     cmd.extend(shlex.split(args))
    442 
    443   p = common.Run(cmd, stdout=subprocess.PIPE)
    444   p.communicate()
    445   assert p.returncode == 0, "bpttool make_table failed"
    446 
    447   img.Write()
    448   bpt.Write()
    449 
    450 
    451 def AddCache(output_zip, prefix="IMAGES/"):
    452   """Create an empty cache image and store it in output_zip."""
    453 
    454   img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "cache.img")
    455   if os.path.exists(img.input_name):
    456     print("cache.img already exists in %s, no need to rebuild..." % (prefix,))
    457     return
    458 
    459   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
    460   # The build system has to explicitly request for cache.img.
    461   if "fs_type" not in image_props:
    462     return
    463 
    464   print("creating cache.img...")
    465 
    466   # Use a fixed timestamp (01/01/2009) when packaging the image.
    467   # Bug: 24377993
    468   epoch = datetime.datetime.fromtimestamp(0)
    469   timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
    470   image_props["timestamp"] = int(timestamp)
    471 
    472   # The name of the directory it is making an image out of matters to
    473   # mkyaffs2image.  So we create a temp dir, and within it we create an
    474   # empty dir named "cache", and build the image from that.
    475   temp_dir = tempfile.mkdtemp()
    476   OPTIONS.tempfiles.append(temp_dir)
    477   user_dir = os.path.join(temp_dir, "cache")
    478   os.mkdir(user_dir)
    479 
    480   fstab = OPTIONS.info_dict["fstab"]
    481   if fstab:
    482     image_props["fs_type"] = fstab["/cache"].fs_type
    483   succ = build_image.BuildImage(user_dir, image_props, img.name)
    484   assert succ, "build cache.img image failed"
    485 
    486   common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
    487   img.Write()
    488 
    489 
    490 def ReplaceUpdatedFiles(zip_filename, files_list):
    491   """Update all the zip entries listed in the files_list.
    492 
    493   For now the list includes META/care_map.txt, and the related files under
    494   SYSTEM/ after rebuilding recovery.
    495   """
    496 
    497   cmd = ["zip", "-d", zip_filename] + files_list
    498   p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    499   p.communicate()
    500 
    501   output_zip = zipfile.ZipFile(zip_filename, "a",
    502                                compression=zipfile.ZIP_DEFLATED,
    503                                allowZip64=True)
    504   for item in files_list:
    505     file_path = os.path.join(OPTIONS.input_tmp, item)
    506     assert os.path.exists(file_path)
    507     common.ZipWrite(output_zip, file_path, arcname=item)
    508   common.ZipClose(output_zip)
    509 
    510 
    511 def AddImagesToTargetFiles(filename):
    512   if os.path.isdir(filename):
    513     OPTIONS.input_tmp = os.path.abspath(filename)
    514     input_zip = None
    515   else:
    516     OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
    517 
    518   if not OPTIONS.add_missing:
    519     if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
    520       print("target_files appears to already contain images.")
    521       sys.exit(1)
    522 
    523   # vendor.img is unlike system.img or system_other.img. Because it could be
    524   # built from source, or dropped into target_files.zip as a prebuilt blob. We
    525   # consider either of them as vendor.img being available, which could be used
    526   # when generating vbmeta.img for AVB.
    527   has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
    528                 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
    529                                             "vendor.img")))
    530   has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
    531                                                 "SYSTEM_OTHER"))
    532 
    533   if input_zip:
    534     OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
    535 
    536     common.ZipClose(input_zip)
    537     output_zip = zipfile.ZipFile(filename, "a",
    538                                  compression=zipfile.ZIP_DEFLATED,
    539                                  allowZip64=True)
    540   else:
    541     OPTIONS.info_dict = common.LoadInfoDict(filename, filename)
    542     output_zip = None
    543     images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
    544     if not os.path.isdir(images_dir):
    545       os.makedirs(images_dir)
    546     images_dir = None
    547 
    548   has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
    549 
    550   if OPTIONS.info_dict.get("avb_enable") == "true":
    551     fp = None
    552     if "build.prop" in OPTIONS.info_dict:
    553       build_prop = OPTIONS.info_dict["build.prop"]
    554       if "ro.build.fingerprint" in build_prop:
    555         fp = build_prop["ro.build.fingerprint"]
    556       elif "ro.build.thumbprint" in build_prop:
    557         fp = build_prop["ro.build.thumbprint"]
    558     if fp:
    559       OPTIONS.info_dict["avb_salt"] = hashlib.sha256(fp).hexdigest()
    560 
    561   def banner(s):
    562     print("\n\n++++ " + s + " ++++\n\n")
    563 
    564   prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    565   boot_image = None
    566   if os.path.exists(prebuilt_path):
    567     banner("boot")
    568     print("boot.img already exists in IMAGES/, no need to rebuild...")
    569     if OPTIONS.rebuild_recovery:
    570       boot_image = common.GetBootableImage(
    571           "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    572   else:
    573     banner("boot")
    574     boot_image = common.GetBootableImage(
    575         "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    576     if boot_image:
    577       if output_zip:
    578         boot_image.AddToZip(output_zip)
    579       else:
    580         boot_image.WriteToDir(OPTIONS.input_tmp)
    581 
    582   recovery_image = None
    583   if has_recovery:
    584     banner("recovery")
    585     prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
    586     if os.path.exists(prebuilt_path):
    587       print("recovery.img already exists in IMAGES/, no need to rebuild...")
    588       if OPTIONS.rebuild_recovery:
    589         recovery_image = common.GetBootableImage(
    590             "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
    591             "RECOVERY")
    592     else:
    593       recovery_image = common.GetBootableImage(
    594           "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
    595       if recovery_image:
    596         if output_zip:
    597           recovery_image.AddToZip(output_zip)
    598         else:
    599           recovery_image.WriteToDir(OPTIONS.input_tmp)
    600 
    601       banner("recovery (two-step image)")
    602       # The special recovery.img for two-step package use.
    603       recovery_two_step_image = common.GetBootableImage(
    604           "IMAGES/recovery-two-step.img", "recovery-two-step.img",
    605           OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
    606       if recovery_two_step_image:
    607         if output_zip:
    608           recovery_two_step_image.AddToZip(output_zip)
    609         else:
    610           recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
    611 
    612   banner("system")
    613   system_img_path = AddSystem(
    614       output_zip, recovery_img=recovery_image, boot_img=boot_image)
    615   vendor_img_path = None
    616   if has_vendor:
    617     banner("vendor")
    618     vendor_img_path = AddVendor(output_zip)
    619   if has_system_other:
    620     banner("system_other")
    621     AddSystemOther(output_zip)
    622   if not OPTIONS.is_signing:
    623     banner("userdata")
    624     AddUserdata(output_zip)
    625     banner("cache")
    626     AddCache(output_zip)
    627 
    628   if OPTIONS.info_dict.get("board_bpt_enable") == "true":
    629     banner("partition-table")
    630     AddPartitionTable(output_zip)
    631 
    632   dtbo_img_path = None
    633   if OPTIONS.info_dict.get("has_dtbo") == "true":
    634     banner("dtbo")
    635     dtbo_img_path = AddDtbo(output_zip)
    636 
    637   if OPTIONS.info_dict.get("avb_enable") == "true":
    638     banner("vbmeta")
    639     boot_contents = boot_image.WriteToTemp()
    640     AddVBMeta(output_zip, boot_contents.name, system_img_path,
    641               vendor_img_path, dtbo_img_path)
    642 
    643   # For devices using A/B update, copy over images from RADIO/ and/or
    644   # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
    645   # images ready under IMAGES/. All images should have '.img' as extension.
    646   banner("radio")
    647   ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
    648   if os.path.exists(ab_partitions):
    649     with open(ab_partitions, 'r') as f:
    650       lines = f.readlines()
    651     # For devices using A/B update, generate care_map for system and vendor
    652     # partitions (if present), then write this file to target_files package.
    653     care_map_list = []
    654     for line in lines:
    655       if line.strip() == "system" and (
    656           "system_verity_block_device" in OPTIONS.info_dict or
    657           OPTIONS.info_dict.get("avb_system_hashtree_enable") == "true"):
    658         assert os.path.exists(system_img_path)
    659         care_map_list += GetCareMap("system", system_img_path)
    660       if line.strip() == "vendor" and (
    661           "vendor_verity_block_device" in OPTIONS.info_dict or
    662           OPTIONS.info_dict.get("avb_vendor_hashtree_enable") == "true"):
    663         assert os.path.exists(vendor_img_path)
    664         care_map_list += GetCareMap("vendor", vendor_img_path)
    665 
    666       img_name = line.strip() + ".img"
    667       prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
    668       if os.path.exists(prebuilt_path):
    669         print("%s already exists, no need to overwrite..." % (img_name,))
    670         continue
    671 
    672       img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
    673       img_vendor_dir = os.path.join(
    674         OPTIONS.input_tmp, "VENDOR_IMAGES")
    675       if os.path.exists(img_radio_path):
    676         if output_zip:
    677           common.ZipWrite(output_zip, img_radio_path,
    678                           os.path.join("IMAGES", img_name))
    679         else:
    680           shutil.copy(img_radio_path, prebuilt_path)
    681       else:
    682         for root, _, files in os.walk(img_vendor_dir):
    683           if img_name in files:
    684             if output_zip:
    685               common.ZipWrite(output_zip, os.path.join(root, img_name),
    686                 os.path.join("IMAGES", img_name))
    687             else:
    688               shutil.copy(os.path.join(root, img_name), prebuilt_path)
    689             break
    690 
    691       if output_zip:
    692         # Zip spec says: All slashes MUST be forward slashes.
    693         img_path = 'IMAGES/' + img_name
    694         assert img_path in output_zip.namelist(), "cannot find " + img_name
    695       else:
    696         img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
    697         assert os.path.exists(img_path), "cannot find " + img_name
    698 
    699     if care_map_list:
    700       care_map_path = "META/care_map.txt"
    701       if output_zip and care_map_path not in output_zip.namelist():
    702         common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list))
    703       else:
    704         with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp:
    705           fp.write('\n'.join(care_map_list))
    706         if output_zip:
    707           OPTIONS.replace_updated_files_list.append(care_map_path)
    708 
    709   # Radio images that need to be packed into IMAGES/, and product-img.zip.
    710   pack_radioimages = os.path.join(
    711       OPTIONS.input_tmp, "META", "pack_radioimages.txt")
    712   if os.path.exists(pack_radioimages):
    713     with open(pack_radioimages, 'r') as f:
    714       lines = f.readlines()
    715     for line in lines:
    716       img_name = line.strip()
    717       _, ext = os.path.splitext(img_name)
    718       if not ext:
    719         img_name += ".img"
    720       prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
    721       if os.path.exists(prebuilt_path):
    722         print("%s already exists, no need to overwrite..." % (img_name,))
    723         continue
    724 
    725       img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
    726       assert os.path.exists(img_radio_path), \
    727           "Failed to find %s at %s" % (img_name, img_radio_path)
    728       if output_zip:
    729         common.ZipWrite(output_zip, img_radio_path,
    730                         os.path.join("IMAGES", img_name))
    731       else:
    732         shutil.copy(img_radio_path, prebuilt_path)
    733 
    734   if output_zip:
    735     common.ZipClose(output_zip)
    736     if OPTIONS.replace_updated_files_list:
    737       ReplaceUpdatedFiles(output_zip.filename,
    738                           OPTIONS.replace_updated_files_list)
    739 
    740 
    741 def main(argv):
    742   def option_handler(o, a):
    743     if o in ("-a", "--add_missing"):
    744       OPTIONS.add_missing = True
    745     elif o in ("-r", "--rebuild_recovery",):
    746       OPTIONS.rebuild_recovery = True
    747     elif o == "--replace_verity_private_key":
    748       OPTIONS.replace_verity_private_key = (True, a)
    749     elif o == "--replace_verity_public_key":
    750       OPTIONS.replace_verity_public_key = (True, a)
    751     elif o == "--is_signing":
    752       OPTIONS.is_signing = True
    753     else:
    754       return False
    755     return True
    756 
    757   args = common.ParseOptions(
    758       argv, __doc__, extra_opts="ar",
    759       extra_long_opts=["add_missing", "rebuild_recovery",
    760                        "replace_verity_public_key=",
    761                        "replace_verity_private_key=",
    762                        "is_signing"],
    763       extra_option_handler=option_handler)
    764 
    765 
    766   if len(args) != 1:
    767     common.Usage(__doc__)
    768     sys.exit(1)
    769 
    770   AddImagesToTargetFiles(args[0])
    771   print("done.")
    772 
    773 if __name__ == '__main__':
    774   try:
    775     common.CloseInheritedPipes()
    776     main(sys.argv[1:])
    777   except common.ExternalError as e:
    778     print("\n   ERROR: %s\n" % (e,))
    779     sys.exit(1)
    780   finally:
    781     common.Cleanup()
    782