github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/make/tools/releasetools/add_img_to_target_files.py (about)

     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 datetime
    49  import os
    50  import shlex
    51  import shutil
    52  import subprocess
    53  import sys
    54  import uuid
    55  import zipfile
    56  
    57  import build_image
    58  import common
    59  import rangelib
    60  import sparse_img
    61  
    62  if sys.hexversion < 0x02070000:
    63    print("Python 2.7 or newer is required.", file=sys.stderr)
    64    sys.exit(1)
    65  
    66  OPTIONS = common.OPTIONS
    67  
    68  OPTIONS.add_missing = False
    69  OPTIONS.rebuild_recovery = False
    70  OPTIONS.replace_updated_files_list = []
    71  OPTIONS.replace_verity_public_key = False
    72  OPTIONS.replace_verity_private_key = False
    73  OPTIONS.is_signing = False
    74  
    75  
    76  # Partitions that should have their care_map added to META/care_map.txt.
    77  PARTITIONS_WITH_CARE_MAP = ('system', 'vendor', 'product')
    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    """Returns the care_map string for the given partition.
   100  
   101    Args:
   102      which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
   103      imgname: The filename of the image.
   104  
   105    Returns:
   106      (which, care_map_ranges): care_map_ranges is the raw string of the care_map
   107      RangeSet.
   108    """
   109    assert which in PARTITIONS_WITH_CARE_MAP
   110  
   111    simg = sparse_img.SparseImage(imgname)
   112    care_map_ranges = simg.care_map
   113    key = which + "_adjusted_partition_size"
   114    adjusted_blocks = OPTIONS.info_dict.get(key)
   115    if adjusted_blocks:
   116      assert adjusted_blocks > 0, "blocks should be positive for " + which
   117      care_map_ranges = care_map_ranges.intersect(rangelib.RangeSet(
   118          "0-%d" % (adjusted_blocks,)))
   119  
   120    return [which, care_map_ranges.to_string_raw()]
   121  
   122  
   123  def AddSystem(output_zip, recovery_img=None, boot_img=None):
   124    """Turn the contents of SYSTEM into a system image and store it in
   125    output_zip. Returns the name of the system image file."""
   126  
   127    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
   128    if os.path.exists(img.input_name):
   129      print("system.img already exists; no need to rebuild...")
   130      return img.input_name
   131  
   132    def output_sink(fn, data):
   133      ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
   134      ofile.write(data)
   135      ofile.close()
   136  
   137      arc_name = "SYSTEM/" + fn
   138      if arc_name in output_zip.namelist():
   139        OPTIONS.replace_updated_files_list.append(arc_name)
   140      else:
   141        common.ZipWrite(output_zip, ofile.name, arc_name)
   142  
   143    if OPTIONS.rebuild_recovery:
   144      print("Building new recovery patch")
   145      common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
   146                               boot_img, info_dict=OPTIONS.info_dict)
   147  
   148    block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map")
   149    CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
   150                block_list=block_list)
   151  
   152    return img.name
   153  
   154  
   155  def AddSystemOther(output_zip):
   156    """Turn the contents of SYSTEM_OTHER into a system_other image
   157    and store it in output_zip."""
   158  
   159    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
   160    if os.path.exists(img.input_name):
   161      print("system_other.img already exists; no need to rebuild...")
   162      return
   163  
   164    CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
   165  
   166  
   167  def AddVendor(output_zip):
   168    """Turn the contents of VENDOR into a vendor image and store in it
   169    output_zip."""
   170  
   171    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
   172    if os.path.exists(img.input_name):
   173      print("vendor.img already exists; no need to rebuild...")
   174      return img.input_name
   175  
   176    block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.map")
   177    CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
   178                block_list=block_list)
   179    return img.name
   180  
   181  
   182  def AddProduct(output_zip):
   183    """Turn the contents of PRODUCT into a product image and store it in
   184    output_zip."""
   185  
   186    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
   187    if os.path.exists(img.input_name):
   188      print("product.img already exists; no need to rebuild...")
   189      return img.input_name
   190  
   191    block_list = OutputFile(
   192        output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
   193    CreateImage(
   194        OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
   195        block_list=block_list)
   196    return img.name
   197  
   198  
   199  def AddDtbo(output_zip):
   200    """Adds the DTBO image.
   201  
   202    Uses the image under IMAGES/ if it already exists. Otherwise looks for the
   203    image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
   204    """
   205    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
   206    if os.path.exists(img.input_name):
   207      print("dtbo.img already exists; no need to rebuild...")
   208      return img.input_name
   209  
   210    dtbo_prebuilt_path = os.path.join(
   211        OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
   212    assert os.path.exists(dtbo_prebuilt_path)
   213    shutil.copy(dtbo_prebuilt_path, img.name)
   214  
   215    # AVB-sign the image as needed.
   216    if OPTIONS.info_dict.get("avb_enable") == "true":
   217      avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
   218      part_size = OPTIONS.info_dict["dtbo_size"]
   219      # The AVB hash footer will be replaced if already present.
   220      cmd = [avbtool, "add_hash_footer", "--image", img.name,
   221             "--partition_size", str(part_size), "--partition_name", "dtbo"]
   222      common.AppendAVBSigningArgs(cmd, "dtbo")
   223      args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
   224      if args and args.strip():
   225        cmd.extend(shlex.split(args))
   226      p = common.Run(cmd, stdout=subprocess.PIPE)
   227      p.communicate()
   228      assert p.returncode == 0, \
   229          "avbtool add_hash_footer of %s failed" % (img.name,)
   230  
   231    img.Write()
   232    return img.name
   233  
   234  
   235  def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
   236    print("creating " + what + ".img...")
   237  
   238    image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
   239    fstab = info_dict["fstab"]
   240    mount_point = "/" + what
   241    if fstab and mount_point in fstab:
   242      image_props["fs_type"] = fstab[mount_point].fs_type
   243  
   244    # Use a fixed timestamp (01/01/2009) when packaging the image.
   245    # Bug: 24377993
   246    epoch = datetime.datetime.fromtimestamp(0)
   247    timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
   248    image_props["timestamp"] = int(timestamp)
   249  
   250    if what == "system":
   251      fs_config_prefix = ""
   252    else:
   253      fs_config_prefix = what + "_"
   254  
   255    fs_config = os.path.join(
   256        input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
   257    if not os.path.exists(fs_config):
   258      fs_config = None
   259  
   260    # Override values loaded from info_dict.
   261    if fs_config:
   262      image_props["fs_config"] = fs_config
   263    if block_list:
   264      image_props["block_list"] = block_list.name
   265  
   266    # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
   267    # build fingerprint).
   268    uuid_seed = what + "-"
   269    if "build.prop" in info_dict:
   270      build_prop = info_dict["build.prop"]
   271      if "ro.build.fingerprint" in build_prop:
   272        uuid_seed += build_prop["ro.build.fingerprint"]
   273      elif "ro.build.thumbprint" in build_prop:
   274        uuid_seed += build_prop["ro.build.thumbprint"]
   275    image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
   276    hash_seed = "hash_seed-" + uuid_seed
   277    image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
   278  
   279    succ = build_image.BuildImage(os.path.join(input_dir, what.upper()),
   280                                  image_props, output_file.name)
   281    assert succ, "build " + what + ".img image failed"
   282  
   283    output_file.Write()
   284    if block_list:
   285      block_list.Write()
   286  
   287    # Set the 'adjusted_partition_size' that excludes the verity blocks of the
   288    # given image. When avb is enabled, this size is the max image size returned
   289    # by the avb tool.
   290    is_verity_partition = "verity_block_device" in image_props
   291    verity_supported = (image_props.get("verity") == "true" or
   292                        image_props.get("avb_enable") == "true")
   293    is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
   294    if verity_supported and (is_verity_partition or is_avb_enable):
   295      adjusted_blocks_value = image_props.get("partition_size")
   296      if adjusted_blocks_value:
   297        adjusted_blocks_key = what + "_adjusted_partition_size"
   298        info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1
   299  
   300  
   301  def AddUserdata(output_zip):
   302    """Create a userdata image and store it in output_zip.
   303  
   304    In most case we just create and store an empty userdata.img;
   305    But the invoker can also request to create userdata.img with real
   306    data from the target files, by setting "userdata_img_with_data=true"
   307    in OPTIONS.info_dict.
   308    """
   309  
   310    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
   311    if os.path.exists(img.input_name):
   312      print("userdata.img already exists; no need to rebuild...")
   313      return
   314  
   315    # Skip userdata.img if no size.
   316    image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
   317    if not image_props.get("partition_size"):
   318      return
   319  
   320    print("creating userdata.img...")
   321  
   322    # Use a fixed timestamp (01/01/2009) when packaging the image.
   323    # Bug: 24377993
   324    epoch = datetime.datetime.fromtimestamp(0)
   325    timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
   326    image_props["timestamp"] = int(timestamp)
   327  
   328    if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
   329      user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
   330    else:
   331      user_dir = common.MakeTempDir()
   332  
   333    fstab = OPTIONS.info_dict["fstab"]
   334    if fstab:
   335      image_props["fs_type"] = fstab["/data"].fs_type
   336    succ = build_image.BuildImage(user_dir, image_props, img.name)
   337    assert succ, "build userdata.img image failed"
   338  
   339    common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
   340    img.Write()
   341  
   342  
   343  def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir):
   344    if not img_path:
   345      return
   346  
   347    # Check if chain partition is used.
   348    key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
   349    if key_path:
   350      # extract public key in AVB format to be included in vbmeta.img
   351      avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
   352      public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition)
   353      p = common.Run([avbtool, "extract_public_key", "--key", key_path,
   354                      "--output", public_key_path],
   355                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   356      p.communicate()
   357      assert p.returncode == 0, \
   358          "avbtool extract_public_key fail for partition: %r" % partition
   359  
   360      rollback_index_location = OPTIONS.info_dict[
   361          "avb_" + partition + "_rollback_index_location"]
   362      cmd.extend(["--chain_partition", "%s:%s:%s" % (
   363          partition, rollback_index_location, public_key_path)])
   364    else:
   365      cmd.extend(["--include_descriptors_from_image", img_path])
   366  
   367  
   368  def AddVBMeta(output_zip, partitions):
   369    """Creates a VBMeta image and store it in output_zip.
   370  
   371    Args:
   372      output_zip: The output zip file, which needs to be already open.
   373      partitions: A dict that's keyed by partition names with image paths as
   374          values. Only valid partition names are accepted, which include 'boot',
   375          'recovery', 'system', 'vendor', 'dtbo'.
   376    """
   377    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vbmeta.img")
   378    if os.path.exists(img.input_name):
   379      print("vbmeta.img already exists; not rebuilding...")
   380      return img.input_name
   381  
   382    avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
   383    cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
   384    common.AppendAVBSigningArgs(cmd, "vbmeta")
   385  
   386    public_key_dir = common.MakeTempDir(prefix="avbpubkey-")
   387    for partition, path in partitions.items():
   388      assert partition in common.AVB_PARTITIONS, 'Unknown partition: %s' % (
   389          partition,)
   390      assert os.path.exists(path), 'Failed to find %s for partition %s' % (
   391          path, partition)
   392      AppendVBMetaArgsForPartition(cmd, partition, path, public_key_dir)
   393  
   394    args = OPTIONS.info_dict.get("avb_vbmeta_args")
   395    if args and args.strip():
   396      split_args = shlex.split(args)
   397      for index, arg in enumerate(split_args[:-1]):
   398        # Sanity check that the image file exists. Some images might be defined
   399        # as a path relative to source tree, which may not be available at the
   400        # same location when running this script (we have the input target_files
   401        # zip only). For such cases, we additionally scan other locations (e.g.
   402        # IMAGES/, RADIO/, etc) before bailing out.
   403        if arg == '--include_descriptors_from_image':
   404          image_path = split_args[index + 1]
   405          if os.path.exists(image_path):
   406            continue
   407          found = False
   408          for dir_name in ['IMAGES', 'RADIO', 'VENDOR_IMAGES', 'PREBUILT_IMAGES']:
   409            alt_path = os.path.join(
   410                OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
   411            if os.path.exists(alt_path):
   412              split_args[index + 1] = alt_path
   413              found = True
   414              break
   415          assert found, 'failed to find %s' % (image_path,)
   416      cmd.extend(split_args)
   417  
   418    p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   419    p.communicate()
   420    assert p.returncode == 0, "avbtool make_vbmeta_image failed"
   421    img.Write()
   422  
   423  
   424  def AddPartitionTable(output_zip):
   425    """Create a partition table image and store it in output_zip."""
   426  
   427    img = OutputFile(
   428        output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
   429    bpt = OutputFile(
   430        output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.bpt")
   431  
   432    # use BPTTOOL from environ, or "bpttool" if empty or not set.
   433    bpttool = os.getenv("BPTTOOL") or "bpttool"
   434    cmd = [bpttool, "make_table", "--output_json", bpt.name,
   435           "--output_gpt", img.name]
   436    input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
   437    input_files = input_files_str.split(" ")
   438    for i in input_files:
   439      cmd.extend(["--input", i])
   440    disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
   441    if disk_size:
   442      cmd.extend(["--disk_size", disk_size])
   443    args = OPTIONS.info_dict.get("board_bpt_make_table_args")
   444    if args:
   445      cmd.extend(shlex.split(args))
   446  
   447    p = common.Run(cmd, stdout=subprocess.PIPE)
   448    p.communicate()
   449    assert p.returncode == 0, "bpttool make_table failed"
   450  
   451    img.Write()
   452    bpt.Write()
   453  
   454  
   455  def AddCache(output_zip):
   456    """Create an empty cache image and store it in output_zip."""
   457  
   458    img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
   459    if os.path.exists(img.input_name):
   460      print("cache.img already exists; no need to rebuild...")
   461      return
   462  
   463    image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
   464    # The build system has to explicitly request for cache.img.
   465    if "fs_type" not in image_props:
   466      return
   467  
   468    print("creating cache.img...")
   469  
   470    # Use a fixed timestamp (01/01/2009) when packaging the image.
   471    # Bug: 24377993
   472    epoch = datetime.datetime.fromtimestamp(0)
   473    timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
   474    image_props["timestamp"] = int(timestamp)
   475  
   476    user_dir = common.MakeTempDir()
   477  
   478    fstab = OPTIONS.info_dict["fstab"]
   479    if fstab:
   480      image_props["fs_type"] = fstab["/cache"].fs_type
   481    succ = build_image.BuildImage(user_dir, image_props, img.name)
   482    assert succ, "build cache.img image failed"
   483  
   484    common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
   485    img.Write()
   486  
   487  
   488  def AddRadioImagesForAbOta(output_zip, ab_partitions):
   489    """Adds the radio images needed for A/B OTA to the output file.
   490  
   491    It parses the list of A/B partitions, looks for the missing ones from RADIO/
   492    or VENDOR_IMAGES/ dirs, and copies them to IMAGES/ of the output file (or
   493    dir).
   494  
   495    It also ensures that on returning from the function all the listed A/B
   496    partitions must have their images available under IMAGES/.
   497  
   498    Args:
   499      output_zip: The output zip file (needs to be already open), or None to
   500          write images to OPTIONS.input_tmp/.
   501      ab_partitions: The list of A/B partitions.
   502  
   503    Raises:
   504      AssertionError: If it can't find an image.
   505    """
   506    for partition in ab_partitions:
   507      img_name = partition.strip() + ".img"
   508      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
   509      if os.path.exists(prebuilt_path):
   510        print("%s already exists, no need to overwrite..." % (img_name,))
   511        continue
   512  
   513      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
   514      if os.path.exists(img_radio_path):
   515        if output_zip:
   516          common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
   517        else:
   518          shutil.copy(img_radio_path, prebuilt_path)
   519        continue
   520  
   521      # Walk through VENDOR_IMAGES/ since files could be under subdirs.
   522      img_vendor_dir = os.path.join(OPTIONS.input_tmp, "VENDOR_IMAGES")
   523      for root, _, files in os.walk(img_vendor_dir):
   524        if img_name in files:
   525          if output_zip:
   526            common.ZipWrite(output_zip, os.path.join(root, img_name),
   527                            "IMAGES/" + img_name)
   528          else:
   529            shutil.copy(os.path.join(root, img_name), prebuilt_path)
   530          break
   531  
   532      # Assert that the image is present under IMAGES/ now.
   533      if output_zip:
   534        # Zip spec says: All slashes MUST be forward slashes.
   535        img_path = 'IMAGES/' + img_name
   536        assert img_path in output_zip.namelist(), "cannot find " + img_name
   537      else:
   538        img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
   539        assert os.path.exists(img_path), "cannot find " + img_name
   540  
   541  
   542  def AddCareMapTxtForAbOta(output_zip, ab_partitions, image_paths):
   543    """Generates and adds care_map.txt for system and vendor partitions.
   544  
   545    Args:
   546      output_zip: The output zip file (needs to be already open), or None to
   547          write images to OPTIONS.input_tmp/.
   548      ab_partitions: The list of A/B partitions.
   549      image_paths: A map from the partition name to the image path.
   550    """
   551    care_map_list = []
   552    for partition in ab_partitions:
   553      partition = partition.strip()
   554      if partition not in PARTITIONS_WITH_CARE_MAP:
   555        continue
   556  
   557      verity_block_device = "{}_verity_block_device".format(partition)
   558      avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
   559      if (verity_block_device in OPTIONS.info_dict or
   560          OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
   561        image_path = image_paths[partition]
   562        assert os.path.exists(image_path)
   563        care_map_list += GetCareMap(partition, image_path)
   564  
   565    if care_map_list:
   566      care_map_path = "META/care_map.txt"
   567      if output_zip and care_map_path not in output_zip.namelist():
   568        common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list))
   569      else:
   570        with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp:
   571          fp.write('\n'.join(care_map_list))
   572        if output_zip:
   573          OPTIONS.replace_updated_files_list.append(care_map_path)
   574  
   575  
   576  def AddPackRadioImages(output_zip, images):
   577    """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
   578  
   579    Args:
   580      output_zip: The output zip file (needs to be already open), or None to
   581          write images to OPTIONS.input_tmp/.
   582      images: A list of image names.
   583  
   584    Raises:
   585      AssertionError: If a listed image can't be found.
   586    """
   587    for image in images:
   588      img_name = image.strip()
   589      _, ext = os.path.splitext(img_name)
   590      if not ext:
   591        img_name += ".img"
   592  
   593      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
   594      if os.path.exists(prebuilt_path):
   595        print("%s already exists, no need to overwrite..." % (img_name,))
   596        continue
   597  
   598      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
   599      assert os.path.exists(img_radio_path), \
   600          "Failed to find %s at %s" % (img_name, img_radio_path)
   601  
   602      if output_zip:
   603        common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
   604      else:
   605        shutil.copy(img_radio_path, prebuilt_path)
   606  
   607  
   608  def ReplaceUpdatedFiles(zip_filename, files_list):
   609    """Updates all the ZIP entries listed in files_list.
   610  
   611    For now the list includes META/care_map.txt, and the related files under
   612    SYSTEM/ after rebuilding recovery.
   613    """
   614    common.ZipDelete(zip_filename, files_list)
   615    output_zip = zipfile.ZipFile(zip_filename, "a",
   616                                 compression=zipfile.ZIP_DEFLATED,
   617                                 allowZip64=True)
   618    for item in files_list:
   619      file_path = os.path.join(OPTIONS.input_tmp, item)
   620      assert os.path.exists(file_path)
   621      common.ZipWrite(output_zip, file_path, arcname=item)
   622    common.ZipClose(output_zip)
   623  
   624  
   625  def AddImagesToTargetFiles(filename):
   626    """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
   627  
   628    It works with either a zip file (zip mode), or a directory that contains the
   629    files to be packed into a target_files.zip (dir mode). The latter is used when
   630    being called from build/make/core/Makefile.
   631  
   632    The images will be created under IMAGES/ in the input target_files.zip.
   633  
   634    Args:
   635      filename: the target_files.zip, or the zip root directory.
   636    """
   637    if os.path.isdir(filename):
   638      OPTIONS.input_tmp = os.path.abspath(filename)
   639    else:
   640      OPTIONS.input_tmp = common.UnzipTemp(filename)
   641  
   642    if not OPTIONS.add_missing:
   643      if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
   644        print("target_files appears to already contain images.")
   645        sys.exit(1)
   646  
   647    OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
   648  
   649    has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
   650  
   651    # {vendor,product}.img is unlike system.img or system_other.img. Because it
   652    # could be built from source, or dropped into target_files.zip as a prebuilt
   653    # blob. We consider either of them as {vendor,product}.img being available,
   654    # which could be used when generating vbmeta.img for AVB.
   655    has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
   656                  os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
   657                                              "vendor.img")))
   658    has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or
   659                   os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
   660                                               "product.img")))
   661    has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
   662                                                  "SYSTEM_OTHER"))
   663  
   664    # Set up the output destination. It writes to the given directory for dir
   665    # mode; otherwise appends to the given ZIP.
   666    if os.path.isdir(filename):
   667      output_zip = None
   668    else:
   669      output_zip = zipfile.ZipFile(filename, "a",
   670                                   compression=zipfile.ZIP_DEFLATED,
   671                                   allowZip64=True)
   672  
   673    # Always make input_tmp/IMAGES available, since we may stage boot / recovery
   674    # images there even under zip mode. The directory will be cleaned up as part
   675    # of OPTIONS.input_tmp.
   676    images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
   677    if not os.path.isdir(images_dir):
   678      os.makedirs(images_dir)
   679  
   680    # A map between partition names and their paths, which could be used when
   681    # generating AVB vbmeta image.
   682    partitions = dict()
   683  
   684    def banner(s):
   685      print("\n\n++++ " + s + " ++++\n\n")
   686  
   687    banner("boot")
   688    # common.GetBootableImage() returns the image directly if present.
   689    boot_image = common.GetBootableImage(
   690        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
   691    # boot.img may be unavailable in some targets (e.g. aosp_arm64).
   692    if boot_image:
   693      partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
   694      if not os.path.exists(partitions['boot']):
   695        boot_image.WriteToDir(OPTIONS.input_tmp)
   696        if output_zip:
   697          boot_image.AddToZip(output_zip)
   698  
   699    recovery_image = None
   700    if has_recovery:
   701      banner("recovery")
   702      recovery_image = common.GetBootableImage(
   703          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
   704      assert recovery_image, "Failed to create recovery.img."
   705      partitions['recovery'] = os.path.join(
   706          OPTIONS.input_tmp, "IMAGES", "recovery.img")
   707      if not os.path.exists(partitions['recovery']):
   708        recovery_image.WriteToDir(OPTIONS.input_tmp)
   709        if output_zip:
   710          recovery_image.AddToZip(output_zip)
   711  
   712        banner("recovery (two-step image)")
   713        # The special recovery.img for two-step package use.
   714        recovery_two_step_image = common.GetBootableImage(
   715            "IMAGES/recovery-two-step.img", "recovery-two-step.img",
   716            OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
   717        assert recovery_two_step_image, "Failed to create recovery-two-step.img."
   718        recovery_two_step_image_path = os.path.join(
   719            OPTIONS.input_tmp, "IMAGES", "recovery-two-step.img")
   720        if not os.path.exists(recovery_two_step_image_path):
   721          recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
   722          if output_zip:
   723            recovery_two_step_image.AddToZip(output_zip)
   724  
   725    banner("system")
   726    partitions['system'] = AddSystem(
   727        output_zip, recovery_img=recovery_image, boot_img=boot_image)
   728  
   729    if has_vendor:
   730      banner("vendor")
   731      partitions['vendor'] = AddVendor(output_zip)
   732  
   733    if has_product:
   734      banner("product")
   735      partitions['product'] = AddProduct(output_zip)
   736  
   737    if has_system_other:
   738      banner("system_other")
   739      AddSystemOther(output_zip)
   740  
   741    if not OPTIONS.is_signing:
   742      banner("userdata")
   743      AddUserdata(output_zip)
   744      banner("cache")
   745      AddCache(output_zip)
   746  
   747    if OPTIONS.info_dict.get("board_bpt_enable") == "true":
   748      banner("partition-table")
   749      AddPartitionTable(output_zip)
   750  
   751    if OPTIONS.info_dict.get("has_dtbo") == "true":
   752      banner("dtbo")
   753      partitions['dtbo'] = AddDtbo(output_zip)
   754  
   755    if OPTIONS.info_dict.get("avb_enable") == "true":
   756      banner("vbmeta")
   757      AddVBMeta(output_zip, partitions)
   758  
   759    banner("radio")
   760    ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
   761                                     "ab_partitions.txt")
   762    if os.path.exists(ab_partitions_txt):
   763      with open(ab_partitions_txt, 'r') as f:
   764        ab_partitions = f.readlines()
   765  
   766      # For devices using A/B update, copy over images from RADIO/ and/or
   767      # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
   768      # images ready under IMAGES/. All images should have '.img' as extension.
   769      AddRadioImagesForAbOta(output_zip, ab_partitions)
   770  
   771      # Generate care_map.txt for system and vendor partitions (if present), then
   772      # write this file to target_files package.
   773      AddCareMapTxtForAbOta(output_zip, ab_partitions, partitions)
   774  
   775    # Radio images that need to be packed into IMAGES/, and product-img.zip.
   776    pack_radioimages_txt = os.path.join(
   777        OPTIONS.input_tmp, "META", "pack_radioimages.txt")
   778    if os.path.exists(pack_radioimages_txt):
   779      with open(pack_radioimages_txt, 'r') as f:
   780        AddPackRadioImages(output_zip, f.readlines())
   781  
   782    if output_zip:
   783      common.ZipClose(output_zip)
   784      if OPTIONS.replace_updated_files_list:
   785        ReplaceUpdatedFiles(output_zip.filename,
   786                            OPTIONS.replace_updated_files_list)
   787  
   788  
   789  def main(argv):
   790    def option_handler(o, a):
   791      if o in ("-a", "--add_missing"):
   792        OPTIONS.add_missing = True
   793      elif o in ("-r", "--rebuild_recovery",):
   794        OPTIONS.rebuild_recovery = True
   795      elif o == "--replace_verity_private_key":
   796        OPTIONS.replace_verity_private_key = (True, a)
   797      elif o == "--replace_verity_public_key":
   798        OPTIONS.replace_verity_public_key = (True, a)
   799      elif o == "--is_signing":
   800        OPTIONS.is_signing = True
   801      else:
   802        return False
   803      return True
   804  
   805    args = common.ParseOptions(
   806        argv, __doc__, extra_opts="ar",
   807        extra_long_opts=["add_missing", "rebuild_recovery",
   808                         "replace_verity_public_key=",
   809                         "replace_verity_private_key=",
   810                         "is_signing"],
   811        extra_option_handler=option_handler)
   812  
   813  
   814    if len(args) != 1:
   815      common.Usage(__doc__)
   816      sys.exit(1)
   817  
   818    AddImagesToTargetFiles(args[0])
   819    print("done.")
   820  
   821  if __name__ == '__main__':
   822    try:
   823      common.CloseInheritedPipes()
   824      main(sys.argv[1:])
   825    except common.ExternalError as e:
   826      print("\n   ERROR: %s\n" % (e,))
   827      sys.exit(1)
   828    finally:
   829      common.Cleanup()