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 and there're separate
     32       boot / recovery images.
     33 
     34   --replace_verity_private_key
     35       Replace the private key used for verity signing. (same as the option
     36       in sign_target_files_apks)
     37 
     38   --replace_verity_public_key
     39        Replace the certificate (public key) used for verity verification. (same
     40        as the option in sign_target_files_apks)
     41 
     42   --is_signing
     43       Skip building & adding the images for "userdata" and "cache" if we
     44       are signing the target files.
     45 """
     46 
     47 from __future__ import print_function
     48 
     49 import datetime
     50 import logging
     51 import os
     52 import shlex
     53 import shutil
     54 import sys
     55 import uuid
     56 import zipfile
     57 
     58 import build_image
     59 import build_super_image
     60 import common
     61 import rangelib
     62 import sparse_img
     63 
     64 if sys.hexversion < 0x02070000:
     65   print("Python 2.7 or newer is required.", file=sys.stderr)
     66   sys.exit(1)
     67 
     68 logger = logging.getLogger(__name__)
     69 
     70 OPTIONS = common.OPTIONS
     71 OPTIONS.add_missing = False
     72 OPTIONS.rebuild_recovery = False
     73 OPTIONS.replace_updated_files_list = []
     74 OPTIONS.replace_verity_public_key = False
     75 OPTIONS.replace_verity_private_key = False
     76 OPTIONS.is_signing = False
     77 
     78 # Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging
     79 # images. (b/24377993, b/80600931)
     80 FIXED_FILE_TIMESTAMP = int((
     81     datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) -
     82     datetime.datetime.utcfromtimestamp(0)).total_seconds())
     83 
     84 
     85 class OutputFile(object):
     86   """A helper class to write a generated file to the given dir or zip.
     87 
     88   When generating images, we want the outputs to go into the given zip file, or
     89   the given dir.
     90 
     91   Attributes:
     92     name: The name of the output file, regardless of the final destination.
     93   """
     94 
     95   def __init__(self, output_zip, input_dir, prefix, name):
     96     # We write the intermediate output file under the given input_dir, even if
     97     # the final destination is a zip archive.
     98     self.name = os.path.join(input_dir, prefix, name)
     99     self._output_zip = output_zip
    100     if self._output_zip:
    101       self._zip_name = os.path.join(prefix, name)
    102 
    103   def Write(self):
    104     if self._output_zip:
    105       common.ZipWrite(self._output_zip, self.name, self._zip_name)
    106 
    107 
    108 def GetCareMap(which, imgname):
    109   """Returns the care_map string for the given partition.
    110 
    111   Args:
    112     which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
    113     imgname: The filename of the image.
    114 
    115   Returns:
    116     (which, care_map_ranges): care_map_ranges is the raw string of the care_map
    117     RangeSet.
    118   """
    119   assert which in common.PARTITIONS_WITH_CARE_MAP
    120 
    121   simg = sparse_img.SparseImage(imgname)
    122   care_map_ranges = simg.care_map
    123   size_key = which + "_image_size"
    124   image_size = OPTIONS.info_dict.get(size_key)
    125   if image_size:
    126     # excludes the verity metadata blocks of the given image. When AVB is enabled,
    127     # this size is the max image size returned by the AVB tool
    128     image_blocks = int(image_size) / 4096 - 1
    129     assert image_blocks > 0, "blocks for {} must be positive".format(which)
    130     care_map_ranges = care_map_ranges.intersect(
    131         rangelib.RangeSet("0-{}".format(image_blocks)))
    132 
    133   return [which, care_map_ranges.to_string_raw()]
    134 
    135 
    136 def AddSystem(output_zip, recovery_img=None, boot_img=None):
    137   """Turn the contents of SYSTEM into a system image and store it in
    138   output_zip. Returns the name of the system image file."""
    139 
    140   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
    141   if os.path.exists(img.name):
    142     logger.info("system.img already exists; no need to rebuild...")
    143     return img.name
    144 
    145   def output_sink(fn, data):
    146     ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
    147     ofile.write(data)
    148     ofile.close()
    149 
    150     if output_zip:
    151       arc_name = "SYSTEM/" + fn
    152       if arc_name in output_zip.namelist():
    153         OPTIONS.replace_updated_files_list.append(arc_name)
    154       else:
    155         common.ZipWrite(output_zip, ofile.name, arc_name)
    156 
    157   if (OPTIONS.rebuild_recovery and recovery_img is not None and
    158       boot_img is not None):
    159     logger.info("Building new recovery patch")
    160     common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
    161                              boot_img, info_dict=OPTIONS.info_dict)
    162 
    163   block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map")
    164   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
    165               block_list=block_list)
    166 
    167   return img.name
    168 
    169 
    170 def AddSystemOther(output_zip):
    171   """Turn the contents of SYSTEM_OTHER into a system_other image
    172   and store it in output_zip."""
    173 
    174   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
    175   if os.path.exists(img.name):
    176     logger.info("system_other.img already exists; no need to rebuild...")
    177     return
    178 
    179   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
    180 
    181 
    182 def AddVendor(output_zip):
    183   """Turn the contents of VENDOR into a vendor image and store in it
    184   output_zip."""
    185 
    186   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
    187   if os.path.exists(img.name):
    188     logger.info("vendor.img already exists; no need to rebuild...")
    189     return img.name
    190 
    191   block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.map")
    192   CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
    193               block_list=block_list)
    194   return img.name
    195 
    196 
    197 def AddProduct(output_zip):
    198   """Turn the contents of PRODUCT into a product image and store it in
    199   output_zip."""
    200 
    201   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
    202   if os.path.exists(img.name):
    203     logger.info("product.img already exists; no need to rebuild...")
    204     return img.name
    205 
    206   block_list = OutputFile(
    207       output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
    208   CreateImage(
    209       OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
    210       block_list=block_list)
    211   return img.name
    212 
    213 
    214 def AddProductServices(output_zip):
    215   """Turn the contents of PRODUCT_SERVICES into a product_services image and
    216   store it in output_zip."""
    217 
    218   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES",
    219                    "product_services.img")
    220   if os.path.exists(img.name):
    221     logger.info("product_services.img already exists; no need to rebuild...")
    222     return img.name
    223 
    224   block_list = OutputFile(
    225       output_zip, OPTIONS.input_tmp, "IMAGES", "product_services.map")
    226   CreateImage(
    227       OPTIONS.input_tmp, OPTIONS.info_dict, "product_services", img,
    228       block_list=block_list)
    229   return img.name
    230 
    231 
    232 def AddOdm(output_zip):
    233   """Turn the contents of ODM into an odm image and store it in output_zip."""
    234 
    235   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img")
    236   if os.path.exists(img.name):
    237     logger.info("odm.img already exists; no need to rebuild...")
    238     return img.name
    239 
    240   block_list = OutputFile(
    241       output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map")
    242   CreateImage(
    243       OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img,
    244       block_list=block_list)
    245   return img.name
    246 
    247 
    248 def AddDtbo(output_zip):
    249   """Adds the DTBO image.
    250 
    251   Uses the image under IMAGES/ if it already exists. Otherwise looks for the
    252   image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
    253   """
    254   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
    255   if os.path.exists(img.name):
    256     logger.info("dtbo.img already exists; no need to rebuild...")
    257     return img.name
    258 
    259   dtbo_prebuilt_path = os.path.join(
    260       OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
    261   assert os.path.exists(dtbo_prebuilt_path)
    262   shutil.copy(dtbo_prebuilt_path, img.name)
    263 
    264   # AVB-sign the image as needed.
    265   if OPTIONS.info_dict.get("avb_enable") == "true":
    266     avbtool = OPTIONS.info_dict["avb_avbtool"]
    267     part_size = OPTIONS.info_dict["dtbo_size"]
    268     # The AVB hash footer will be replaced if already present.
    269     cmd = [avbtool, "add_hash_footer", "--image", img.name,
    270            "--partition_size", str(part_size), "--partition_name", "dtbo"]
    271     common.AppendAVBSigningArgs(cmd, "dtbo")
    272     args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
    273     if args and args.strip():
    274       cmd.extend(shlex.split(args))
    275     common.RunAndCheckOutput(cmd)
    276 
    277   img.Write()
    278   return img.name
    279 
    280 
    281 def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
    282   logger.info("creating " + what + ".img...")
    283 
    284   image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
    285   fstab = info_dict["fstab"]
    286   mount_point = "/" + what
    287   if fstab and mount_point in fstab:
    288     image_props["fs_type"] = fstab[mount_point].fs_type
    289 
    290   image_props["timestamp"] = FIXED_FILE_TIMESTAMP
    291 
    292   if what == "system":
    293     fs_config_prefix = ""
    294   else:
    295     fs_config_prefix = what + "_"
    296 
    297   fs_config = os.path.join(
    298       input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
    299   if not os.path.exists(fs_config):
    300     fs_config = None
    301 
    302   # Override values loaded from info_dict.
    303   if fs_config:
    304     image_props["fs_config"] = fs_config
    305   if block_list:
    306     image_props["block_list"] = block_list.name
    307 
    308   # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
    309   # build fingerprint).
    310   uuid_seed = what + "-"
    311   if "build.prop" in info_dict:
    312     build_prop = info_dict["build.prop"]
    313     if "ro.build.fingerprint" in build_prop:
    314       uuid_seed += build_prop["ro.build.fingerprint"]
    315     elif "ro.build.thumbprint" in build_prop:
    316       uuid_seed += build_prop["ro.build.thumbprint"]
    317   image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
    318   hash_seed = "hash_seed-" + uuid_seed
    319   image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
    320 
    321   build_image.BuildImage(
    322       os.path.join(input_dir, what.upper()), image_props, output_file.name)
    323 
    324   output_file.Write()
    325   if block_list:
    326     block_list.Write()
    327 
    328   # Set the '_image_size' for given image size.
    329   is_verity_partition = "verity_block_device" in image_props
    330   verity_supported = (image_props.get("verity") == "true" or
    331                       image_props.get("avb_enable") == "true")
    332   is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
    333   if verity_supported and (is_verity_partition or is_avb_enable):
    334     image_size = image_props.get("image_size")
    335     if image_size:
    336       image_size_key = what + "_image_size"
    337       info_dict[image_size_key] = int(image_size)
    338 
    339   use_dynamic_size = (
    340       info_dict.get("use_dynamic_partition_size") == "true" and
    341       what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
    342   if use_dynamic_size:
    343     info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
    344 
    345 
    346 def AddUserdata(output_zip):
    347   """Create a userdata image and store it in output_zip.
    348 
    349   In most case we just create and store an empty userdata.img;
    350   But the invoker can also request to create userdata.img with real
    351   data from the target files, by setting "userdata_img_with_data=true"
    352   in OPTIONS.info_dict.
    353   """
    354 
    355   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
    356   if os.path.exists(img.name):
    357     logger.info("userdata.img already exists; no need to rebuild...")
    358     return
    359 
    360   # Skip userdata.img if no size.
    361   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
    362   if not image_props.get("partition_size"):
    363     return
    364 
    365   logger.info("creating userdata.img...")
    366 
    367   image_props["timestamp"] = FIXED_FILE_TIMESTAMP
    368 
    369   if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
    370     user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
    371   else:
    372     user_dir = common.MakeTempDir()
    373 
    374   fstab = OPTIONS.info_dict["fstab"]
    375   if fstab:
    376     image_props["fs_type"] = fstab["/data"].fs_type
    377   build_image.BuildImage(user_dir, image_props, img.name)
    378 
    379   common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
    380   img.Write()
    381 
    382 
    383 def AppendVBMetaArgsForPartition(cmd, partition, image):
    384   """Appends the VBMeta arguments for partition.
    385 
    386   It sets up the VBMeta argument by including the partition descriptor from the
    387   given 'image', or by configuring the partition as a chained partition.
    388 
    389   Args:
    390     cmd: A list of command args that will be used to generate the vbmeta image.
    391         The argument for the partition will be appended to the list.
    392     partition: The name of the partition (e.g. "system").
    393     image: The path to the partition image.
    394   """
    395   # Check if chain partition is used.
    396   key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
    397   if key_path:
    398     chained_partition_arg = common.GetAvbChainedPartitionArg(
    399         partition, OPTIONS.info_dict)
    400     cmd.extend(["--chain_partition", chained_partition_arg])
    401   else:
    402     cmd.extend(["--include_descriptors_from_image", image])
    403 
    404 
    405 def AddVBMeta(output_zip, partitions, name, needed_partitions):
    406   """Creates a VBMeta image and stores it in output_zip.
    407 
    408   It generates the requested VBMeta image. The requested image could be for
    409   top-level or chained VBMeta image, which is determined based on the name.
    410 
    411   Args:
    412     output_zip: The output zip file, which needs to be already open.
    413     partitions: A dict that's keyed by partition names with image paths as
    414         values. Only valid partition names are accepted, as listed in
    415         common.AVB_PARTITIONS.
    416     name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
    417     needed_partitions: Partitions whose descriptors should be included into the
    418         generated VBMeta image.
    419 
    420   Returns:
    421     Path to the created image.
    422 
    423   Raises:
    424     AssertionError: On invalid input args.
    425   """
    426   assert needed_partitions, "Needed partitions must be specified"
    427 
    428   img = OutputFile(
    429       output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
    430   if os.path.exists(img.name):
    431     logger.info("%s.img already exists; not rebuilding...", name)
    432     return img.name
    433 
    434   avbtool = OPTIONS.info_dict["avb_avbtool"]
    435   cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
    436   common.AppendAVBSigningArgs(cmd, name)
    437 
    438   for partition, path in partitions.items():
    439     if partition not in needed_partitions:
    440       continue
    441     assert (partition in common.AVB_PARTITIONS or
    442             partition.startswith('vbmeta_')), \
    443         'Unknown partition: {}'.format(partition)
    444     assert os.path.exists(path), \
    445         'Failed to find {} for {}'.format(path, partition)
    446     AppendVBMetaArgsForPartition(cmd, partition, path)
    447 
    448   args = OPTIONS.info_dict.get("avb_{}_args".format(name))
    449   if args and args.strip():
    450     split_args = shlex.split(args)
    451     for index, arg in enumerate(split_args[:-1]):
    452       # Sanity check that the image file exists. Some images might be defined
    453       # as a path relative to source tree, which may not be available at the
    454       # same location when running this script (we have the input target_files
    455       # zip only). For such cases, we additionally scan other locations (e.g.
    456       # IMAGES/, RADIO/, etc) before bailing out.
    457       if arg == '--include_descriptors_from_image':
    458         image_path = split_args[index + 1]
    459         if os.path.exists(image_path):
    460           continue
    461         found = False
    462         for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
    463           alt_path = os.path.join(
    464               OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
    465           if os.path.exists(alt_path):
    466             split_args[index + 1] = alt_path
    467             found = True
    468             break
    469         assert found, 'Failed to find {}'.format(image_path)
    470     cmd.extend(split_args)
    471 
    472   common.RunAndCheckOutput(cmd)
    473   img.Write()
    474   return img.name
    475 
    476 
    477 def AddPartitionTable(output_zip):
    478   """Create a partition table image and store it in output_zip."""
    479 
    480   img = OutputFile(
    481       output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
    482   bpt = OutputFile(
    483       output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
    484 
    485   # use BPTTOOL from environ, or "bpttool" if empty or not set.
    486   bpttool = os.getenv("BPTTOOL") or "bpttool"
    487   cmd = [bpttool, "make_table", "--output_json", bpt.name,
    488          "--output_gpt", img.name]
    489   input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
    490   input_files = input_files_str.split(" ")
    491   for i in input_files:
    492     cmd.extend(["--input", i])
    493   disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
    494   if disk_size:
    495     cmd.extend(["--disk_size", disk_size])
    496   args = OPTIONS.info_dict.get("board_bpt_make_table_args")
    497   if args:
    498     cmd.extend(shlex.split(args))
    499   common.RunAndCheckOutput(cmd)
    500 
    501   img.Write()
    502   bpt.Write()
    503 
    504 
    505 def AddCache(output_zip):
    506   """Create an empty cache image and store it in output_zip."""
    507 
    508   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
    509   if os.path.exists(img.name):
    510     logger.info("cache.img already exists; no need to rebuild...")
    511     return
    512 
    513   image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
    514   # The build system has to explicitly request for cache.img.
    515   if "fs_type" not in image_props:
    516     return
    517 
    518   logger.info("creating cache.img...")
    519 
    520   image_props["timestamp"] = FIXED_FILE_TIMESTAMP
    521 
    522   user_dir = common.MakeTempDir()
    523 
    524   fstab = OPTIONS.info_dict["fstab"]
    525   if fstab:
    526     image_props["fs_type"] = fstab["/cache"].fs_type
    527   build_image.BuildImage(user_dir, image_props, img.name)
    528 
    529   common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
    530   img.Write()
    531 
    532 
    533 def CheckAbOtaImages(output_zip, ab_partitions):
    534   """Checks that all the listed A/B partitions have their images available.
    535 
    536   The images need to be available under IMAGES/ or RADIO/, with the former takes
    537   a priority.
    538 
    539   Args:
    540     output_zip: The output zip file (needs to be already open), or None to
    541         find images in OPTIONS.input_tmp/.
    542     ab_partitions: The list of A/B partitions.
    543 
    544   Raises:
    545     AssertionError: If it can't find an image.
    546   """
    547   for partition in ab_partitions:
    548     img_name = partition.strip() + ".img"
    549 
    550     # Assert that the image is present under IMAGES/ now.
    551     if output_zip:
    552       # Zip spec says: All slashes MUST be forward slashes.
    553       images_path = "IMAGES/" + img_name
    554       radio_path = "RADIO/" + img_name
    555       available = (images_path in output_zip.namelist() or
    556                    radio_path in output_zip.namelist())
    557     else:
    558       images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
    559       radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
    560       available = os.path.exists(images_path) or os.path.exists(radio_path)
    561 
    562     assert available, "Failed to find " + img_name
    563 
    564 
    565 def AddCareMapForAbOta(output_zip, ab_partitions, image_paths):
    566   """Generates and adds care_map.pb for a/b partition that has care_map.
    567 
    568   Args:
    569     output_zip: The output zip file (needs to be already open), or None to
    570         write care_map.pb to OPTIONS.input_tmp/.
    571     ab_partitions: The list of A/B partitions.
    572     image_paths: A map from the partition name to the image path.
    573   """
    574   care_map_list = []
    575   for partition in ab_partitions:
    576     partition = partition.strip()
    577     if partition not in common.PARTITIONS_WITH_CARE_MAP:
    578       continue
    579 
    580     verity_block_device = "{}_verity_block_device".format(partition)
    581     avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
    582     if (verity_block_device in OPTIONS.info_dict or
    583         OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
    584       image_path = image_paths[partition]
    585       assert os.path.exists(image_path)
    586       care_map_list += GetCareMap(partition, image_path)
    587 
    588       # adds fingerprint field to the care_map
    589       build_props = OPTIONS.info_dict.get(partition + ".build.prop", {})
    590       prop_name_list = ["ro.{}.build.fingerprint".format(partition),
    591                         "ro.{}.build.thumbprint".format(partition)]
    592 
    593       present_props = [x for x in prop_name_list if x in build_props]
    594       if not present_props:
    595         logger.warning("fingerprint is not present for partition %s", partition)
    596         property_id, fingerprint = "unknown", "unknown"
    597       else:
    598         property_id = present_props[0]
    599         fingerprint = build_props[property_id]
    600       care_map_list += [property_id, fingerprint]
    601 
    602   if not care_map_list:
    603     return
    604 
    605   # Converts the list into proto buf message by calling care_map_generator; and
    606   # writes the result to a temp file.
    607   temp_care_map_text = common.MakeTempFile(prefix="caremap_text-",
    608                                            suffix=".txt")
    609   with open(temp_care_map_text, 'w') as text_file:
    610     text_file.write('\n'.join(care_map_list))
    611 
    612   temp_care_map = common.MakeTempFile(prefix="caremap-", suffix=".pb")
    613   care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map]
    614   common.RunAndCheckOutput(care_map_gen_cmd)
    615 
    616   care_map_path = "META/care_map.pb"
    617   if output_zip and care_map_path not in output_zip.namelist():
    618     common.ZipWrite(output_zip, temp_care_map, arcname=care_map_path)
    619   else:
    620     shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))
    621     if output_zip:
    622       OPTIONS.replace_updated_files_list.append(care_map_path)
    623 
    624 
    625 def AddPackRadioImages(output_zip, images):
    626   """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
    627 
    628   Args:
    629     output_zip: The output zip file (needs to be already open), or None to
    630         write images to OPTIONS.input_tmp/.
    631     images: A list of image names.
    632 
    633   Raises:
    634     AssertionError: If a listed image can't be found.
    635   """
    636   for image in images:
    637     img_name = image.strip()
    638     _, ext = os.path.splitext(img_name)
    639     if not ext:
    640       img_name += ".img"
    641 
    642     prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
    643     if os.path.exists(prebuilt_path):
    644       logger.info("%s already exists, no need to overwrite...", img_name)
    645       continue
    646 
    647     img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
    648     assert os.path.exists(img_radio_path), \
    649         "Failed to find %s at %s" % (img_name, img_radio_path)
    650 
    651     if output_zip:
    652       common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
    653     else:
    654       shutil.copy(img_radio_path, prebuilt_path)
    655 
    656 
    657 def AddSuperEmpty(output_zip):
    658   """Create a super_empty.img and store it in output_zip."""
    659 
    660   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
    661   build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
    662   img.Write()
    663 
    664 
    665 def AddSuperSplit(output_zip):
    666   """Create split super_*.img and store it in output_zip."""
    667 
    668   outdir = os.path.join(OPTIONS.input_tmp, "OTA")
    669   built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
    670 
    671   if built:
    672     for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
    673       img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
    674                        "super_" + dev + ".img")
    675       img.Write()
    676 
    677 
    678 def ReplaceUpdatedFiles(zip_filename, files_list):
    679   """Updates all the ZIP entries listed in files_list.
    680 
    681   For now the list includes META/care_map.pb, and the related files under
    682   SYSTEM/ after rebuilding recovery.
    683   """
    684   common.ZipDelete(zip_filename, files_list)
    685   output_zip = zipfile.ZipFile(zip_filename, "a",
    686                                compression=zipfile.ZIP_DEFLATED,
    687                                allowZip64=True)
    688   for item in files_list:
    689     file_path = os.path.join(OPTIONS.input_tmp, item)
    690     assert os.path.exists(file_path)
    691     common.ZipWrite(output_zip, file_path, arcname=item)
    692   common.ZipClose(output_zip)
    693 
    694 
    695 def AddImagesToTargetFiles(filename):
    696   """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
    697 
    698   It works with either a zip file (zip mode), or a directory that contains the
    699   files to be packed into a target_files.zip (dir mode). The latter is used when
    700   being called from build/make/core/Makefile.
    701 
    702   The images will be created under IMAGES/ in the input target_files.zip.
    703 
    704   Args:
    705     filename: the target_files.zip, or the zip root directory.
    706   """
    707   if os.path.isdir(filename):
    708     OPTIONS.input_tmp = os.path.abspath(filename)
    709   else:
    710     OPTIONS.input_tmp = common.UnzipTemp(filename)
    711 
    712   if not OPTIONS.add_missing:
    713     if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
    714       logger.warning("target_files appears to already contain images.")
    715       sys.exit(1)
    716 
    717   OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
    718 
    719   has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
    720 
    721   # {vendor,odm,product,product_services}.img are unlike system.img or
    722   # system_other.img. Because it could be built from source, or dropped into
    723   # target_files.zip as a prebuilt blob. We consider either of them as
    724   # {vendor,product,product_services}.img being available, which could be
    725   # used when generating vbmeta.img for AVB.
    726   has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
    727                 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
    728                                             "vendor.img")))
    729   has_odm = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) or
    730              os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
    731                                          "odm.img")))
    732   has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or
    733                  os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
    734                                              "product.img")))
    735   has_product_services = (os.path.isdir(os.path.join(OPTIONS.input_tmp,
    736                                                      "PRODUCT_SERVICES")) or
    737                           os.path.exists(os.path.join(OPTIONS.input_tmp,
    738                                                       "IMAGES",
    739                                                       "product_services.img")))
    740   has_system = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM"))
    741   has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
    742                                                 "SYSTEM_OTHER"))
    743 
    744   # Set up the output destination. It writes to the given directory for dir
    745   # mode; otherwise appends to the given ZIP.
    746   if os.path.isdir(filename):
    747     output_zip = None
    748   else:
    749     output_zip = zipfile.ZipFile(filename, "a",
    750                                  compression=zipfile.ZIP_DEFLATED,
    751                                  allowZip64=True)
    752 
    753   # Always make input_tmp/IMAGES available, since we may stage boot / recovery
    754   # images there even under zip mode. The directory will be cleaned up as part
    755   # of OPTIONS.input_tmp.
    756   images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
    757   if not os.path.isdir(images_dir):
    758     os.makedirs(images_dir)
    759 
    760   # A map between partition names and their paths, which could be used when
    761   # generating AVB vbmeta image.
    762   partitions = dict()
    763 
    764   def banner(s):
    765     logger.info("\n\n++++ " + s + " ++++\n\n")
    766 
    767   banner("boot")
    768   # common.GetBootableImage() returns the image directly if present.
    769   boot_image = common.GetBootableImage(
    770       "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    771   # boot.img may be unavailable in some targets (e.g. aosp_arm64).
    772   if boot_image:
    773     partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    774     if not os.path.exists(partitions['boot']):
    775       boot_image.WriteToDir(OPTIONS.input_tmp)
    776       if output_zip:
    777         boot_image.AddToZip(output_zip)
    778 
    779   recovery_image = None
    780   if has_recovery:
    781     banner("recovery")
    782     recovery_image = common.GetBootableImage(
    783         "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
    784     assert recovery_image, "Failed to create recovery.img."
    785     partitions['recovery'] = os.path.join(
    786         OPTIONS.input_tmp, "IMAGES", "recovery.img")
    787     if not os.path.exists(partitions['recovery']):
    788       recovery_image.WriteToDir(OPTIONS.input_tmp)
    789       if output_zip:
    790         recovery_image.AddToZip(output_zip)
    791 
    792       banner("recovery (two-step image)")
    793       # The special recovery.img for two-step package use.
    794       recovery_two_step_image = common.GetBootableImage(
    795           "IMAGES/recovery-two-step.img", "recovery-two-step.img",
    796           OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
    797       assert recovery_two_step_image, "Failed to create recovery-two-step.img."
    798       recovery_two_step_image_path = os.path.join(
    799           OPTIONS.input_tmp, "IMAGES", "recovery-two-step.img")
    800       if not os.path.exists(recovery_two_step_image_path):
    801         recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
    802         if output_zip:
    803           recovery_two_step_image.AddToZip(output_zip)
    804 
    805   if has_system:
    806     banner("system")
    807     partitions['system'] = AddSystem(
    808         output_zip, recovery_img=recovery_image, boot_img=boot_image)
    809 
    810   if has_vendor:
    811     banner("vendor")
    812     partitions['vendor'] = AddVendor(output_zip)
    813 
    814   if has_product:
    815     banner("product")
    816     partitions['product'] = AddProduct(output_zip)
    817 
    818   if has_product_services:
    819     banner("product_services")
    820     partitions['product_services'] = AddProductServices(output_zip)
    821 
    822   if has_odm:
    823     banner("odm")
    824     partitions['odm'] = AddOdm(output_zip)
    825 
    826   if has_system_other:
    827     banner("system_other")
    828     AddSystemOther(output_zip)
    829 
    830   if not OPTIONS.is_signing:
    831     banner("userdata")
    832     AddUserdata(output_zip)
    833     banner("cache")
    834     AddCache(output_zip)
    835 
    836   if OPTIONS.info_dict.get("board_bpt_enable") == "true":
    837     banner("partition-table")
    838     AddPartitionTable(output_zip)
    839 
    840   if OPTIONS.info_dict.get("has_dtbo") == "true":
    841     banner("dtbo")
    842     partitions['dtbo'] = AddDtbo(output_zip)
    843 
    844   if OPTIONS.info_dict.get("avb_enable") == "true":
    845     # vbmeta_partitions includes the partitions that should be included into
    846     # top-level vbmeta.img, which are the ones that are not included in any
    847     # chained VBMeta image plus the chained VBMeta images themselves.
    848     vbmeta_partitions = common.AVB_PARTITIONS[:]
    849 
    850     vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
    851     if vbmeta_system:
    852       banner("vbmeta_system")
    853       partitions["vbmeta_system"] = AddVBMeta(
    854           output_zip, partitions, "vbmeta_system", vbmeta_system.split())
    855       vbmeta_partitions = [
    856           item for item in vbmeta_partitions
    857           if item not in vbmeta_system.split()]
    858       vbmeta_partitions.append("vbmeta_system")
    859 
    860     vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
    861     if vbmeta_vendor:
    862       banner("vbmeta_vendor")
    863       partitions["vbmeta_vendor"] = AddVBMeta(
    864           output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
    865       vbmeta_partitions = [
    866           item for item in vbmeta_partitions
    867           if item not in vbmeta_vendor.split()]
    868       vbmeta_partitions.append("vbmeta_vendor")
    869 
    870     banner("vbmeta")
    871     AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
    872 
    873   if OPTIONS.info_dict.get("build_super_partition") == "true":
    874     banner("super_empty")
    875     AddSuperEmpty(output_zip)
    876 
    877     if OPTIONS.info_dict.get(
    878         "build_retrofit_dynamic_partitions_ota_package") == "true":
    879       banner("super split images")
    880       AddSuperSplit(output_zip)
    881 
    882   banner("radio")
    883   ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
    884                                    "ab_partitions.txt")
    885   if os.path.exists(ab_partitions_txt):
    886     with open(ab_partitions_txt, 'r') as f:
    887       ab_partitions = f.readlines()
    888 
    889     # For devices using A/B update, make sure we have all the needed images
    890     # ready under IMAGES/ or RADIO/.
    891     CheckAbOtaImages(output_zip, ab_partitions)
    892 
    893     # Generate care_map.pb for ab_partitions, then write this file to
    894     # target_files package.
    895     AddCareMapForAbOta(output_zip, ab_partitions, partitions)
    896 
    897   # Radio images that need to be packed into IMAGES/, and product-img.zip.
    898   pack_radioimages_txt = os.path.join(
    899       OPTIONS.input_tmp, "META", "pack_radioimages.txt")
    900   if os.path.exists(pack_radioimages_txt):
    901     with open(pack_radioimages_txt, 'r') as f:
    902       AddPackRadioImages(output_zip, f.readlines())
    903 
    904   if output_zip:
    905     common.ZipClose(output_zip)
    906     if OPTIONS.replace_updated_files_list:
    907       ReplaceUpdatedFiles(output_zip.filename,
    908                           OPTIONS.replace_updated_files_list)
    909 
    910 
    911 def main(argv):
    912   def option_handler(o, a):
    913     if o in ("-a", "--add_missing"):
    914       OPTIONS.add_missing = True
    915     elif o in ("-r", "--rebuild_recovery",):
    916       OPTIONS.rebuild_recovery = True
    917     elif o == "--replace_verity_private_key":
    918       OPTIONS.replace_verity_private_key = (True, a)
    919     elif o == "--replace_verity_public_key":
    920       OPTIONS.replace_verity_public_key = (True, a)
    921     elif o == "--is_signing":
    922       OPTIONS.is_signing = True
    923     else:
    924       return False
    925     return True
    926 
    927   args = common.ParseOptions(
    928       argv, __doc__, extra_opts="ar",
    929       extra_long_opts=["add_missing", "rebuild_recovery",
    930                        "replace_verity_public_key=",
    931                        "replace_verity_private_key=",
    932                        "is_signing"],
    933       extra_option_handler=option_handler)
    934 
    935   if len(args) != 1:
    936     common.Usage(__doc__)
    937     sys.exit(1)
    938 
    939   common.InitLogging()
    940 
    941   AddImagesToTargetFiles(args[0])
    942   logger.info("done.")
    943 
    944 if __name__ == '__main__':
    945   try:
    946     common.CloseInheritedPipes()
    947     main(sys.argv[1:])
    948   except common.ExternalError:
    949     logger.exception("\n   ERROR:\n")
    950     sys.exit(1)
    951   finally:
    952     common.Cleanup()
    953