aboutsummaryrefslogtreecommitdiff
path: root/tools/releasetools/add_img_to_target_files.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/releasetools/add_img_to_target_files.py')
-rw-r--r--tools/releasetools/add_img_to_target_files.py302
1 files changed, 160 insertions, 142 deletions
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index f58b6978a8..c583d013b3 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -51,6 +51,7 @@ import logging
import os
import shlex
import shutil
+import stat
import sys
import uuid
import zipfile
@@ -58,9 +59,11 @@ import zipfile
import build_image
import build_super_image
import common
-import rangelib
-import sparse_img
import verity_utils
+import ota_metadata_pb2
+
+from apex_utils import GetApexInfoFromTargetFiles
+from common import AddCareMapForAbOta
if sys.hexversion < 0x02070000:
print("Python 2.7 or newer is required.", file=sys.stderr)
@@ -93,58 +96,19 @@ class OutputFile(object):
name: The name of the output file, regardless of the final destination.
"""
- def __init__(self, output_zip, input_dir, prefix, name):
+ def __init__(self, output_zip, input_dir, *args):
# We write the intermediate output file under the given input_dir, even if
# the final destination is a zip archive.
- self.name = os.path.join(input_dir, prefix, name)
+ self.name = os.path.join(input_dir, *args)
self._output_zip = output_zip
if self._output_zip:
- self._zip_name = os.path.join(prefix, name)
+ self._zip_name = os.path.join(*args)
def Write(self):
if self._output_zip:
common.ZipWrite(self._output_zip, self.name, self._zip_name)
-def GetCareMap(which, imgname):
- """Returns the care_map string for the given partition.
-
- Args:
- which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
- imgname: The filename of the image.
-
- Returns:
- (which, care_map_ranges): care_map_ranges is the raw string of the care_map
- RangeSet; or None.
- """
- assert which in common.PARTITIONS_WITH_CARE_MAP
-
- # which + "_image_size" contains the size that the actual filesystem image
- # resides in, which is all that needs to be verified. The additional blocks in
- # the image file contain verity metadata, by reading which would trigger
- # invalid reads.
- image_size = OPTIONS.info_dict.get(which + "_image_size")
- if not image_size:
- return None
-
- image_blocks = int(image_size) // 4096 - 1
- assert image_blocks > 0, "blocks for {} must be positive".format(which)
-
- # For sparse images, we will only check the blocks that are listed in the care
- # map, i.e. the ones with meaningful data.
- if "extfs_sparse_flag" in OPTIONS.info_dict:
- simg = sparse_img.SparseImage(imgname)
- care_map_ranges = simg.care_map.intersect(
- rangelib.RangeSet("0-{}".format(image_blocks)))
-
- # Otherwise for non-sparse images, we read all the blocks in the filesystem
- # image.
- else:
- care_map_ranges = rangelib.RangeSet("0-{}".format(image_blocks))
-
- return [which, care_map_ranges.to_string_raw()]
-
-
def AddSystem(output_zip, recovery_img=None, boot_img=None):
"""Turn the contents of SYSTEM into a system image and store it in
output_zip. Returns the name of the system image file."""
@@ -178,7 +142,6 @@ def AddSystem(output_zip, recovery_img=None, boot_img=None):
block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map")
CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
block_list=block_list)
-
return img.name
@@ -281,6 +244,38 @@ def AddOdm(output_zip):
return img.name
+def AddVendorDlkm(output_zip):
+ """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip."""
+
+ img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img")
+ if os.path.exists(img.name):
+ logger.info("vendor_dlkm.img already exists; no need to rebuild...")
+ return img.name
+
+ block_list = OutputFile(
+ output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map")
+ CreateImage(
+ OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img,
+ block_list=block_list)
+ return img.name
+
+
+def AddOdmDlkm(output_zip):
+ """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip."""
+
+ img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img")
+ if os.path.exists(img.name):
+ logger.info("odm_dlkm.img already exists; no need to rebuild...")
+ return img.name
+
+ block_list = OutputFile(
+ output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map")
+ CreateImage(
+ OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img,
+ block_list=block_list)
+ return img.name
+
+
def AddDtbo(output_zip):
"""Adds the DTBO image.
@@ -299,6 +294,9 @@ def AddDtbo(output_zip):
# AVB-sign the image as needed.
if OPTIONS.info_dict.get("avb_enable") == "true":
+ # Signing requires +w
+ os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
+
avbtool = OPTIONS.info_dict["avb_avbtool"]
part_size = OPTIONS.info_dict["dtbo_size"]
# The AVB hash footer will be replaced if already present.
@@ -313,6 +311,43 @@ def AddDtbo(output_zip):
img.Write()
return img.name
+
+def AddPvmfw(output_zip):
+ """Adds the pvmfw image.
+
+ Uses the image under IMAGES/ if it already exists. Otherwise looks for the
+ image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
+ """
+ img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img")
+ if os.path.exists(img.name):
+ logger.info("pvmfw.img already exists; no need to rebuild...")
+ return img.name
+
+ pvmfw_prebuilt_path = os.path.join(
+ OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img")
+ assert os.path.exists(pvmfw_prebuilt_path)
+ shutil.copy(pvmfw_prebuilt_path, img.name)
+
+ # AVB-sign the image as needed.
+ if OPTIONS.info_dict.get("avb_enable") == "true":
+ # Signing requires +w
+ os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
+
+ avbtool = OPTIONS.info_dict["avb_avbtool"]
+ part_size = OPTIONS.info_dict["pvmfw_size"]
+ # The AVB hash footer will be replaced if already present.
+ cmd = [avbtool, "add_hash_footer", "--image", img.name,
+ "--partition_size", str(part_size), "--partition_name", "pvmfw"]
+ common.AppendAVBSigningArgs(cmd, "pvmfw")
+ args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args")
+ if args and args.strip():
+ cmd.extend(shlex.split(args))
+ common.RunAndCheckOutput(cmd)
+
+ img.Write()
+ return img.name
+
+
def AddCustomImages(output_zip, partition_name):
"""Adds and signs custom images in IMAGES/.
@@ -327,8 +362,6 @@ def AddCustomImages(output_zip, partition_name):
AssertionError: If image can't be found.
"""
- partition_size = OPTIONS.info_dict.get(
- "avb_{}_partition_size".format(partition_name))
key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
extra_args = OPTIONS.info_dict.get(
@@ -387,8 +420,9 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
image_props["block_list"] = block_list.name
# Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
- # build fingerprint).
- build_info = common.BuildInfo(info_dict)
+ # build fingerprint). Also use the legacy build id, because the vbmeta digest
+ # isn't available at this point.
+ build_info = common.BuildInfo(info_dict, use_legacy_id=True)
uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
hash_seed = "hash_seed-" + uuid_seed
@@ -572,72 +606,6 @@ def CheckAbOtaImages(output_zip, ab_partitions):
assert available, "Failed to find " + img_name
-def AddCareMapForAbOta(output_zip, ab_partitions, image_paths):
- """Generates and adds care_map.pb for a/b partition that has care_map.
-
- Args:
- output_zip: The output zip file (needs to be already open), or None to
- write care_map.pb to OPTIONS.input_tmp/.
- ab_partitions: The list of A/B partitions.
- image_paths: A map from the partition name to the image path.
- """
- care_map_list = []
- for partition in ab_partitions:
- partition = partition.strip()
- if partition not in common.PARTITIONS_WITH_CARE_MAP:
- continue
-
- verity_block_device = "{}_verity_block_device".format(partition)
- avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
- if (verity_block_device in OPTIONS.info_dict or
- OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
- image_path = image_paths[partition]
- assert os.path.exists(image_path)
-
- care_map = GetCareMap(partition, image_path)
- if not care_map:
- continue
- care_map_list += care_map
-
- # adds fingerprint field to the care_map
- # TODO(xunchang) revisit the fingerprint calculation for care_map.
- partition_props = OPTIONS.info_dict.get(partition + ".build.prop")
- prop_name_list = ["ro.{}.build.fingerprint".format(partition),
- "ro.{}.build.thumbprint".format(partition)]
-
- present_props = [x for x in prop_name_list if
- partition_props and partition_props.GetProp(x)]
- if not present_props:
- logger.warning("fingerprint is not present for partition %s", partition)
- property_id, fingerprint = "unknown", "unknown"
- else:
- property_id = present_props[0]
- fingerprint = partition_props.GetProp(property_id)
- care_map_list += [property_id, fingerprint]
-
- if not care_map_list:
- return
-
- # Converts the list into proto buf message by calling care_map_generator; and
- # writes the result to a temp file.
- temp_care_map_text = common.MakeTempFile(prefix="caremap_text-",
- suffix=".txt")
- with open(temp_care_map_text, 'w') as text_file:
- text_file.write('\n'.join(care_map_list))
-
- temp_care_map = common.MakeTempFile(prefix="caremap-", suffix=".pb")
- care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map]
- common.RunAndCheckOutput(care_map_gen_cmd)
-
- care_map_path = "META/care_map.pb"
- if output_zip and care_map_path not in output_zip.namelist():
- common.ZipWrite(output_zip, temp_care_map, arcname=care_map_path)
- else:
- shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))
- if output_zip:
- OPTIONS.replace_updated_files_list.append(care_map_path)
-
-
def AddPackRadioImages(output_zip, images):
"""Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
@@ -708,6 +676,34 @@ def ReplaceUpdatedFiles(zip_filename, files_list):
common.ZipClose(output_zip)
+def HasPartition(partition_name):
+ """Determines if the target files archive should build a given partition."""
+
+ return ((os.path.isdir(
+ os.path.join(OPTIONS.input_tmp, partition_name.upper())) and
+ OPTIONS.info_dict.get(
+ "building_{}_image".format(partition_name)) == "true") or
+ os.path.exists(
+ os.path.join(OPTIONS.input_tmp, "IMAGES",
+ "{}.img".format(partition_name))))
+
+def AddApexInfo(output_zip):
+ apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system')
+ apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
+ apex_metadata_proto.apex_info.extend(apex_infos)
+ apex_info_bytes = apex_metadata_proto.SerializeToString()
+
+ output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb")
+ with open(output_file, "wb") as ofile:
+ ofile.write(apex_info_bytes)
+ if output_zip:
+ arc_name = "META/apex_info.pb"
+ if arc_name in output_zip.namelist():
+ OPTIONS.replace_updated_files_list.append(arc_name)
+ else:
+ common.ZipWrite(output_zip, output_file, arc_name)
+
+
def AddImagesToTargetFiles(filename):
"""Creates and adds images (boot/recovery/system/...) to a target_files.zip.
@@ -736,28 +732,18 @@ def AddImagesToTargetFiles(filename):
has_boot = OPTIONS.info_dict.get("no_boot") != "true"
has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
- # {vendor,odm,product,system_ext}.img are unlike system.img or
- # system_other.img. Because it could be built from source, or dropped into
- # target_files.zip as a prebuilt blob. We consider either of them as
- # {vendor,product,system_ext}.img being available, which could be
- # used when generating vbmeta.img for AVB.
- has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
- os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
- "vendor.img")))
- has_odm = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) or
- os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
- "odm.img")))
- has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or
- os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
- "product.img")))
- has_system_ext = (os.path.isdir(os.path.join(OPTIONS.input_tmp,
- "SYSTEM_EXT")) or
- os.path.exists(os.path.join(OPTIONS.input_tmp,
- "IMAGES",
- "system_ext.img")))
- has_system = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM"))
- has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
- "SYSTEM_OTHER"))
+ # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system, system_other}.img
+ # can be built from source, or dropped into target_files.zip as a prebuilt blob.
+ has_vendor = HasPartition("vendor")
+ has_odm = HasPartition("odm")
+ has_vendor_dlkm = HasPartition("vendor_dlkm")
+ has_odm_dlkm = HasPartition("odm_dlkm")
+ has_product = HasPartition("product")
+ has_system_ext = HasPartition("system_ext")
+ has_system = HasPartition("system")
+ has_system_other = HasPartition("system_other")
+ has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
+ has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
# Set up the output destination. It writes to the given directory for dir
# mode; otherwise appends to the given ZIP.
@@ -865,10 +851,20 @@ def AddImagesToTargetFiles(filename):
banner("odm")
partitions['odm'] = AddOdm(output_zip)
+ if has_vendor_dlkm:
+ banner("vendor_dlkm")
+ partitions['vendor_dlkm'] = AddVendorDlkm(output_zip)
+
+ if has_odm_dlkm:
+ banner("odm_dlkm")
+ partitions['odm_dlkm'] = AddOdmDlkm(output_zip)
+
if has_system_other:
banner("system_other")
AddSystemOther(output_zip)
+ AddApexInfo(output_zip)
+
if not OPTIONS.is_signing:
banner("userdata")
AddUserdata(output_zip)
@@ -883,6 +879,10 @@ def AddImagesToTargetFiles(filename):
banner("dtbo")
partitions['dtbo'] = AddDtbo(output_zip)
+ if OPTIONS.info_dict.get("has_pvmfw") == "true":
+ banner("pvmfw")
+ partitions['pvmfw'] = AddPvmfw(output_zip)
+
# Custom images.
custom_partitions = OPTIONS.info_dict.get(
"avb_custom_images_partition_list", "").strip().split()
@@ -918,12 +918,14 @@ def AddImagesToTargetFiles(filename):
if item not in vbmeta_vendor.split()]
vbmeta_partitions.append("vbmeta_vendor")
- banner("vbmeta")
- AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
+ if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true":
+ banner("vbmeta")
+ AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
- banner("super_empty")
- AddSuperEmpty(output_zip)
+ if OPTIONS.info_dict.get("build_super_empty_partition") == "true":
+ banner("super_empty")
+ AddSuperEmpty(output_zip)
if OPTIONS.info_dict.get("build_super_partition") == "true":
if OPTIONS.info_dict.get(
@@ -944,7 +946,9 @@ def AddImagesToTargetFiles(filename):
# Generate care_map.pb for ab_partitions, then write this file to
# target_files package.
- AddCareMapForAbOta(output_zip, ab_partitions, partitions)
+ output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb")
+ AddCareMapForAbOta(output_zip if output_zip else output_care_map,
+ ab_partitions, partitions)
# Radio images that need to be packed into IMAGES/, and product-img.zip.
pack_radioimages_txt = os.path.join(
@@ -953,6 +957,20 @@ def AddImagesToTargetFiles(filename):
with open(pack_radioimages_txt) as f:
AddPackRadioImages(output_zip, f.readlines())
+ # Calculate the vbmeta digest and put the result in to META/
+ boot_images = OPTIONS.info_dict.get("boot_images")
+ # Disable the digest calculation if the target_file is used as a container
+ # for boot images.
+ boot_container = boot_images and len(boot_images.split()) >= 2
+ if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
+ OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
+ avbtool = OPTIONS.info_dict["avb_avbtool"]
+ digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)
+ vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META",
+ "vbmeta_digest.txt")
+ with open(vbmeta_digest_txt, 'w') as f:
+ f.write(digest)
+
if output_zip:
common.ZipClose(output_zip)
if OPTIONS.replace_updated_files_list: