Merge "Add sepolicy compat versions to soong config"
diff --git a/core/Makefile b/core/Makefile
index 26ed864..ea5fc49 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -527,16 +527,6 @@
     $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,,$(kmd)))))
 
 # -----------------------------------------------------------------
-# FSVerity metadata generation
-ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
-
-FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)
-FSVERITY_APK_OUT := system/etc/security/fsverity/BuildManifest.apk
-FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml
-
-endif # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
-
-# -----------------------------------------------------------------
 # Cert-to-package mapping.  Used by the post-build signing tools.
 # Use a macro to add newline to each echo command
 # $1 stem name of the package
@@ -1744,11 +1734,6 @@
 $(if $(filter $(2),system),\
     $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1))
     $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity=$(HOST_OUT_EXECUTABLES)/fsverity" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_generate_metadata=true" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_key=$(FSVERITY_APK_KEY_PATH)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_manifest=$(FSVERITY_APK_MANIFEST_PATH)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_out=$(FSVERITY_APK_OUT)" >> $(1))
     $(call add-common-ro-flags-to-image-props,system,$(1))
 )
 $(if $(filter $(2),system_other),\
@@ -2765,6 +2750,56 @@
 # -----------------------------------------------------------------
 # system image
 
+# FSVerity metadata generation
+# Generate fsverity metadata files (.fsv_meta) and build manifest
+# (system/etc/security/fsverity/BuildManifest.apk) BEFORE filtering systemimage files below
+ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
+
+# Generate fsv_meta
+fsverity-metadata-targets := $(sort $(filter \
+  $(TARGET_OUT)/framework/%.jar \
+  $(foreach arch,$(TARGET_ARCH) $(TARGET_2ND_ARCH),$(foreach ext,oat vdex art, \
+    $(TARGET_OUT)/framework/oat/$(arch)/%.$(ext))) \
+  $(TARGET_OUT)/etc/boot-image.prof \
+  $(TARGET_OUT)/etc/dirty-image-objects \
+  $(TARGET_OUT)/etc/updatable-bcp-packages.txt, \
+  $(ALL_GENERATED_SOURCES) $(ALL_DEFAULT_INSTALLED_MODULES)))
+
+define fsverity-generate-metadata
+$(1).fsv_meta: PRIVATE_SRC := $(1)
+$(1).fsv_meta: PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity
+$(1).fsv_meta: $(HOST_OUT_EXECUTABLES)/fsverity_metadata_generator $(HOST_OUT_EXECUTABLES)/fsverity $(1)
+	$$< --fsverity-path $$(PRIVATE_FSVERITY) --signature none \
+	    --hash-alg sha256 --output $$@ $$(PRIVATE_SRC)
+endef
+
+$(foreach f,$(fsverity-metadata-targets),$(eval $(call fsverity-generate-metadata,$(f))))
+ALL_DEFAULT_INSTALLED_MODULES += $(addsuffix .fsv_meta,$(fsverity-metadata-targets))
+
+# Generate BuildManifest.apk
+FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)
+FSVERITY_APK_OUT := $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk
+FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml
+$(FSVERITY_APK_OUT): PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity
+$(FSVERITY_APK_OUT): PRIVATE_AAPT2 := $(HOST_OUT_EXECUTABLES)/aapt2
+$(FSVERITY_APK_OUT): PRIVATE_APKSIGNER := $(HOST_OUT_EXECUTABLES)/apksigner
+$(FSVERITY_APK_OUT): PRIVATE_MANIFEST := $(FSVERITY_APK_MANIFEST_PATH)
+$(FSVERITY_APK_OUT): PRIVATE_KEY := $(FSVERITY_APK_KEY_PATH)
+$(FSVERITY_APK_OUT): PRIVATE_INPUTS := $(fsverity-metadata-targets)
+$(FSVERITY_APK_OUT): $(HOST_OUT_EXECUTABLES)/fsverity_manifest_generator \
+    $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 \
+    $(HOST_OUT_EXECUTABLES)/apksigner $(FSVERITY_APK_MANIFEST_PATH) \
+    $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8 \
+    $(fsverity-metadata-targets)
+	$< --fsverity-path $(PRIVATE_FSVERITY) --aapt2-path $(PRIVATE_AAPT2) \
+	    --apksigner-path $(PRIVATE_APKSIGNER) --apk-key-path $(PRIVATE_KEY) \
+	    --apk-manifest-path $(PRIVATE_MANIFEST) --output $@ \
+	    --base-dir $(PRODUCT_OUT) $(PRIVATE_INPUTS)
+
+ALL_DEFAULT_INSTALLED_MODULES += $(FSVERITY_APK_OUT)
+
+endif  # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
+
 INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \
     $(ALL_GENERATED_SOURCES) \
     $(ALL_DEFAULT_INSTALLED_MODULES)))
@@ -2864,10 +2899,6 @@
 ifeq ($(BOARD_AVB_ENABLE),true)
 $(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
 endif
-ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
-$(BUILT_SYSTEMIMAGE): $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 $(HOST_OUT_EXECUTABLES)/apksigner \
-    $(FSVERITY_APK_MANIFEST_PATH) $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8
-endif
 $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
 	$(call build-systemimage-target,$@)
 
@@ -4953,6 +4984,10 @@
 # image.
 ifdef BUILDING_SYSTEM_IMAGE
   $(BUILT_TARGET_FILES_PACKAGE): $(FULL_SYSTEMIMAGE_DEPS)
+else
+  # releasetools may need the system build.prop even when building a
+  # system-image-less product.
+  $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BUILD_PROP_TARGET)
 endif
 
 ifdef BUILDING_USERDATA_IMAGE
@@ -5199,6 +5234,12 @@
 	@# Contents of the system image
 	$(hide) $(call package_files-copy-root, \
 	    $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
+else ifdef INSTALLED_BUILD_PROP_TARGET
+	@# Copy the system build.prop even if not building a system image
+	@# because add_img_to_target_files may need it to build other partition
+	@# images.
+	$(hide) mkdir -p "$(zip_root)/SYSTEM"
+	$(hide) cp "$(INSTALLED_BUILD_PROP_TARGET)" "$(patsubst $(TARGET_OUT)/%,$(zip_root)/SYSTEM/%,$(INSTALLED_BUILD_PROP_TARGET))"
 endif
 ifdef BUILDING_USERDATA_IMAGE
 	@# Contents of the data image
@@ -5338,7 +5379,7 @@
 	$(hide) cp $(INSTALLED_INIT_BOOT_IMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
 endif
 ifndef BOARD_PREBUILT_BOOTIMAGE
-ifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))
+ifneq (,$(strip $(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES))))
 ifdef INSTALLED_BOOTIMAGE_TARGET
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 8a5440f..cec7792 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -981,6 +981,18 @@
 ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \
     $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES)
 
+ifdef LOCAL_TEST_DATA
+  # Export the list of targets that are handled as data inputs and required
+  # by tests at runtime. The LOCAL_TEST_DATA format is generated from below
+  # https://cs.android.com/android/platform/superproject/+/master:build/soong/android/androidmk.go;l=925-944;drc=master
+  # which format is like $(path):$(relative_file) but for module-info, only
+  # the string after ":" is needed.
+  ALL_MODULES.$(my_register_name).TEST_DATA := \
+    $(strip $(ALL_MODULES.$(my_register_name).TEST_DATA) \
+      $(foreach f, $(LOCAL_TEST_DATA),\
+        $(call word-colon,2,$(f))))
+endif
+
 ##########################################################################
 ## When compiling against the VNDK, add the .vendor or .product suffix to
 ## required modules.
diff --git a/core/product.mk b/core/product.mk
index b069dda..7192226 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -445,7 +445,7 @@
 
 # Install a copy of the debug policy to the system_ext partition, and allow
 # init-second-stage to load debug policy from system_ext.
-# This option is only meant to be set by GSI products.
+# This option is only meant to be set by compliance GSI targets.
 _product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
 
 # If set, metadata files for the following artifacts will be generated.
diff --git a/core/product_config.mk b/core/product_config.mk
index 57b7724..6fae73e 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -420,9 +420,15 @@
   $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))
 endif
 
+# Show a warning wall of text if non-compliance-GSI products set this option.
 ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
-  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64,$(PRODUCT_NAME)))
-    $(error Only GSI products are allowed to set PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
+  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64,$(PRODUCT_NAME)))
+    $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \
+      PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \
+      testing. This is a special configuration for compliance GSI, so do make \
+      sure you understand the security implications before setting this \
+      option. If you don't know what this option does, then you probably \
+      shouldn't set this.)
   endif
 endif
 
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 5d5bfa8..aeeb403 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -23,6 +23,7 @@
 			'"classes_jar": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)", )], ' \
 			'"test_mainline_modules": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES)),"$(w)", )], ' \
 			'"is_unit_test": "$(ALL_MODULES.$(m).IS_UNIT_TEST)", ' \
+			'"data": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA)),"$(w)", )], ' \
 			'},\n' \
 	 ) | sed -e 's/, *\]/]/g' -e 's/, *\}/ }/g' -e '$$s/,$$//' >> $@
 	$(hide) echo '}' >> $@
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index bf19c5c..051de62 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -85,7 +85,7 @@
     #  It must be of the form "YYYY-MM-DD" on production devices.
     #  It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
     #  If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-      PLATFORM_SECURITY_PATCH := 2021-12-05
+      PLATFORM_SECURITY_PATCH := 2022-01-05
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 94aaea0..3cad6f1 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -56,9 +56,11 @@
 VNDK-SP: libutils.so
 VNDK-SP: libutilscallstack.so
 VNDK-SP: libz.so
+VNDK-core: android.hardware.audio.common-V1-ndk.so
 VNDK-core: android.hardware.audio.common@2.0.so
 VNDK-core: android.hardware.authsecret-V1-ndk.so
 VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so
+VNDK-core: android.hardware.bluetooth.audio-V1-ndk.so
 VNDK-core: android.hardware.configstore-utils.so
 VNDK-core: android.hardware.configstore@1.0.so
 VNDK-core: android.hardware.configstore@1.1.so
@@ -104,6 +106,7 @@
 VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so
 VNDK-core: android.hidl.token@1.0-utils.so
 VNDK-core: android.hidl.token@1.0.so
+VNDK-core: android.media.audio.common.types-V1-ndk.so
 VNDK-core: android.system.keystore2-V1-ndk.so
 VNDK-core: android.system.suspend-V1-ndk.so
 VNDK-core: android.system.suspend@1.0.so
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index bf7f9a0..f3123b2 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -554,12 +554,25 @@
 }
 
 python_binary_host {
-    name: "fsverity_metadata_generator",
+    name: "fsverity_manifest_generator",
     srcs: [
-        "fsverity_metadata_generator.py",
+        "fsverity_manifest_generator.py",
     ],
     libs: [
         "fsverity_digests_proto_python",
+        "releasetools_common",
+    ],
+    required: [
+        "aapt2",
+        "apksigner",
+        "fsverity",
+    ],
+}
+
+python_binary_host {
+    name: "fsverity_metadata_generator",
+    srcs: [
+        "fsverity_metadata_generator.py",
     ],
     required: [
         "fsverity",
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 34aa1a6..a4377c7 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -35,9 +35,6 @@
 import common
 import verity_utils
 
-from fsverity_digests_pb2 import FSVerityDigests
-from fsverity_metadata_generator import FSVerityMetadataGenerator
-
 logger = logging.getLogger(__name__)
 
 OPTIONS = common.OPTIONS
@@ -451,69 +448,6 @@
 
   return mkfs_output
 
-def GenerateFSVerityMetadata(in_dir, fsverity_path, apk_key_path, apk_manifest_path, apk_out_path):
-  """Generates fsverity metadata files.
-
-  By setting PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA := true, fsverity
-  metadata files will be generated. For the input files, see `patterns` below.
-
-  One metadata file per one input file will be generated with the suffix
-  .fsv_meta. e.g. system/framework/foo.jar -> system/framework/foo.jar.fsv_meta
-  Also a mapping file containing fsverity digests will be generated to
-  system/etc/security/fsverity/BuildManifest.apk.
-
-  Args:
-    in_dir: temporary working directory (same as BuildImage)
-    fsverity_path: path to host tool fsverity
-    apk_key_path: path to key (e.g. build/make/target/product/security/platform)
-    apk_manifest_path: path to AndroidManifest.xml for APK
-    apk_out_path: path to the output APK
-
-  Returns:
-    None. The files are generated directly under in_dir.
-  """
-
-  patterns = [
-    "system/framework/*.jar",
-    "system/framework/oat/*/*.oat",
-    "system/framework/oat/*/*.vdex",
-    "system/framework/oat/*/*.art",
-    "system/etc/boot-image.prof",
-    "system/etc/dirty-image-objects",
-  ]
-  files = []
-  for pattern in patterns:
-    files += glob.glob(os.path.join(in_dir, pattern))
-  files = sorted(set(files))
-
-  generator = FSVerityMetadataGenerator(fsverity_path)
-  generator.set_hash_alg("sha256")
-
-  digests = FSVerityDigests()
-  for f in files:
-    generator.generate(f)
-    # f is a full path for now; make it relative so it starts with {mount_point}/
-    digest = digests.digests[os.path.relpath(f, in_dir)]
-    digest.digest = generator.digest(f)
-    digest.hash_alg = "sha256"
-
-  temp_dir = common.MakeTempDir()
-
-  os.mkdir(os.path.join(temp_dir, "assets"))
-  metadata_path = os.path.join(temp_dir, "assets", "build_manifest")
-  with open(metadata_path, "wb") as f:
-    f.write(digests.SerializeToString())
-
-  apk_path = os.path.join(in_dir, apk_out_path)
-
-  common.RunAndCheckOutput(["aapt2", "link",
-      "-A", os.path.join(temp_dir, "assets"),
-      "-o", apk_path,
-      "--manifest", apk_manifest_path])
-  common.RunAndCheckOutput(["apksigner", "sign", "--in", apk_path,
-      "--cert", apk_key_path + ".x509.pem",
-      "--key", apk_key_path + ".pk8"])
-
 def BuildImage(in_dir, prop_dict, out_file, target_out=None):
   """Builds an image for the files under in_dir and writes it to out_file.
 
@@ -541,13 +475,6 @@
   elif fs_type.startswith("f2fs") and prop_dict.get("f2fs_compress") == "true":
     fs_spans_partition = False
 
-  if "fsverity_generate_metadata" in prop_dict:
-    GenerateFSVerityMetadata(in_dir,
-        fsverity_path=prop_dict["fsverity"],
-        apk_key_path=prop_dict["fsverity_apk_key"],
-        apk_manifest_path=prop_dict["fsverity_apk_manifest"],
-        apk_out_path=prop_dict["fsverity_apk_out"])
-
   # Get a builder for creating an image that's to be verified by Verified Boot,
   # or None if not applicable.
   verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
@@ -801,11 +728,6 @@
     copy_prop("system_root_image", "system_root_image")
     copy_prop("root_dir", "root_dir")
     copy_prop("root_fs_config", "root_fs_config")
-    copy_prop("fsverity", "fsverity")
-    copy_prop("fsverity_generate_metadata", "fsverity_generate_metadata")
-    copy_prop("fsverity_apk_key","fsverity_apk_key")
-    copy_prop("fsverity_apk_manifest","fsverity_apk_manifest")
-    copy_prop("fsverity_apk_out","fsverity_apk_out")
   elif mount_point == "data":
     # Copy the generic fs type first, override with specific one if available.
     copy_prop("flash_logical_block_size", "flash_logical_block_size")
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 6ec1b94..30dcf5b 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -73,6 +73,7 @@
         self.search_path = os.environ["ANDROID_HOST_OUT"]
     self.signapk_shared_library_path = "lib64"   # Relative to search_path
     self.extra_signapk_args = []
+    self.aapt2_path = "aapt2"
     self.java_path = "java"  # Use the one on the path by default.
     self.java_args = ["-Xmx2048m"]  # The default JVM args.
     self.android_jar_path = None
@@ -1699,8 +1700,8 @@
   Args:
     image_path: The full path of the image, e.g., /path/to/boot.img.
     prebuilt_name: The prebuilt image name, e.g., boot.img, boot-5.4-gz.img,
-        boot-5.10.img, recovery.img.
-    partition_name: The partition name, e.g., 'boot' or 'recovery'.
+        boot-5.10.img, recovery.img or init_boot.img.
+    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
     info_dict: The information dict read from misc_info.txt.
   """
   if info_dict is None:
@@ -1724,6 +1725,35 @@
     RunAndCheckOutput(cmd)
 
 
+def HasRamdisk(partition_name, info_dict=None):
+  """Returns true/false to see if a bootable image should have a ramdisk.
+
+  Args:
+    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
+    info_dict: The information dict read from misc_info.txt.
+  """
+  if info_dict is None:
+    info_dict = OPTIONS.info_dict
+
+  if partition_name != "boot":
+    return True  # init_boot.img or recovery.img has a ramdisk.
+
+  if info_dict.get("recovery_as_boot") == "true":
+    return True  # the recovery-as-boot boot.img has a RECOVERY ramdisk.
+
+  if info_dict.get("system_root_image") == "true":
+    # The ramdisk content is merged into the system.img, so there is NO
+    # ramdisk in the boot.img or boot-<kernel version>.img.
+    return False
+
+  if info_dict.get("init_boot") == "true":
+    # The ramdisk is moved to the init_boot.img, so there is NO
+    # ramdisk in the boot.img or boot-<kernel version>.img.
+    return False
+
+  return True
+
+
 def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
                      info_dict=None, two_step_image=False):
   """Return a File object with the desired bootable image.
@@ -1745,25 +1775,18 @@
     logger.info("using prebuilt %s from IMAGES...", prebuilt_name)
     return File.FromLocalFile(name, prebuilt_path)
 
+  partition_name = tree_subdir.lower()
   prebuilt_path = os.path.join(unpack_dir, "PREBUILT_IMAGES", prebuilt_name)
   if os.path.exists(prebuilt_path):
     logger.info("Re-signing prebuilt %s from PREBUILT_IMAGES...", prebuilt_name)
     signed_img = MakeTempFile()
     shutil.copy(prebuilt_path, signed_img)
-    partition_name = tree_subdir.lower()
     _SignBootableImage(signed_img, prebuilt_name, partition_name, info_dict)
     return File.FromLocalFile(name, signed_img)
 
   logger.info("building image from target_files %s...", tree_subdir)
 
-  # With system_root_image == "true", we don't pack ramdisk into the boot image.
-  # With init_boot == "true", we don't pack the ramdisk into boot.img.
-  # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
-  # for recovery.
-  has_ramdisk = ((info_dict.get("system_root_image") != "true" and
-                  info_dict.get("init_boot") != "true") or
-                 prebuilt_name != "boot.img" or
-                 info_dict.get("recovery_as_boot") == "true")
+  has_ramdisk = HasRamdisk(partition_name, info_dict)
 
   fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
   data = _BuildBootableImage(prebuilt_name, os.path.join(unpack_dir, tree_subdir),
@@ -2162,8 +2185,8 @@
 def GetMinSdkVersion(apk_name):
   """Gets the minSdkVersion declared in the APK.
 
-  It calls 'aapt2' to query the embedded minSdkVersion from the given APK file.
-  This can be both a decimal number (API Level) or a codename.
+  It calls OPTIONS.aapt2_path to query the embedded minSdkVersion from the given
+  APK file. This can be both a decimal number (API Level) or a codename.
 
   Args:
     apk_name: The APK filename.
@@ -2175,7 +2198,7 @@
     ExternalError: On failing to obtain the min SDK version.
   """
   proc = Run(
-      ["aapt2", "dump", "badging", apk_name], stdout=subprocess.PIPE,
+      [OPTIONS.aapt2_path, "dump", "badging", apk_name], stdout=subprocess.PIPE,
       stderr=subprocess.PIPE)
   stdoutdata, stderrdata = proc.communicate()
   if proc.returncode != 0:
@@ -2451,7 +2474,7 @@
     opts, args = getopt.getopt(
         argv, "hvp:s:x:" + extra_opts,
         ["help", "verbose", "path=", "signapk_path=",
-         "signapk_shared_library_path=", "extra_signapk_args=",
+         "signapk_shared_library_path=", "extra_signapk_args=", "aapt2_path=",
          "java_path=", "java_args=", "android_jar_path=", "public_key_suffix=",
          "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
          "verity_signer_path=", "verity_signer_args=", "device_specific=",
@@ -2475,6 +2498,8 @@
       OPTIONS.signapk_shared_library_path = a
     elif o in ("--extra_signapk_args",):
       OPTIONS.extra_signapk_args = shlex.split(a)
+    elif o in ("--aapt2_path",):
+      OPTIONS.aapt2_path = a
     elif o in ("--java_path",):
       OPTIONS.java_path = a
     elif o in ("--java_args",):
@@ -3870,7 +3895,10 @@
   disable_sparse = OPTIONS.info_dict.get(which + "_disable_sparse")
 
   image_blocks = int(image_size) // 4096 - 1
-  assert image_blocks > 0, "blocks for {} must be positive".format(which)
+  # It's OK for image_blocks to be 0, because care map ranges are inclusive.
+  # So 0-0 means "just block 0", which is valid.
+  assert image_blocks >= 0, "blocks for {} must be non-negative, image size: {}".format(
+      which, image_size)
 
   # For sparse images, we will only check the blocks that are listed in the care
   # map, i.e. the ones with meaningful data.
diff --git a/tools/releasetools/fsverity_manifest_generator.py b/tools/releasetools/fsverity_manifest_generator.py
new file mode 100644
index 0000000..e61e257
--- /dev/null
+++ b/tools/releasetools/fsverity_manifest_generator.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+`fsverity_manifest_generator` generates build manifest APK file containing
+digests of target files. The APK file is signed so the manifest inside the APK
+can be trusted.
+"""
+
+import argparse
+import common
+import os
+import subprocess
+import sys
+from fsverity_digests_pb2 import FSVerityDigests
+
+HASH_ALGORITHM = 'sha256'
+
+def _digest(fsverity_path, input_file):
+  cmd = [fsverity_path, 'digest', input_file]
+  cmd.extend(['--compact'])
+  cmd.extend(['--hash-alg', HASH_ALGORITHM])
+  out = subprocess.check_output(cmd, universal_newlines=True).strip()
+  return bytes(bytearray.fromhex(out))
+
+if __name__ == '__main__':
+  p = argparse.ArgumentParser()
+  p.add_argument(
+      '--output',
+      help='Path to the output manifest APK',
+      required=True)
+  p.add_argument(
+      '--fsverity-path',
+      help='path to the fsverity program',
+      required=True)
+  p.add_argument(
+      '--aapt2-path',
+      help='path to the aapt2 program',
+      required=True)
+  p.add_argument(
+      '--apksigner-path',
+      help='path to the apksigner program',
+      required=True)
+  p.add_argument(
+      '--apk-key-path',
+      help='path to the apk key',
+      required=True)
+  p.add_argument(
+      '--apk-manifest-path',
+      help='path to AndroidManifest.xml',
+      required=True)
+  p.add_argument(
+      '--base-dir',
+      help='directory to use as a relative root for the inputs',
+      required=True)
+  p.add_argument(
+      'inputs',
+      nargs='+',
+      help='input file for the build manifest')
+  args = p.parse_args(sys.argv[1:])
+
+  digests = FSVerityDigests()
+  for f in sorted(args.inputs):
+    # f is a full path for now; make it relative so it starts with {mount_point}/
+    digest = digests.digests[os.path.relpath(f, args.base_dir)]
+    digest.digest = _digest(args.fsverity_path, f)
+    digest.hash_alg = HASH_ALGORITHM
+
+  temp_dir = common.MakeTempDir()
+
+  os.mkdir(os.path.join(temp_dir, "assets"))
+  metadata_path = os.path.join(temp_dir, "assets", "build_manifest.pb")
+  with open(metadata_path, "wb") as f:
+    f.write(digests.SerializeToString())
+
+  common.RunAndCheckOutput([args.aapt2_path, "link",
+      "-A", os.path.join(temp_dir, "assets"),
+      "-o", args.output,
+      "--manifest", args.apk_manifest_path])
+  common.RunAndCheckOutput([args.apksigner_path, "sign", "--in", args.output,
+      "--cert", args.apk_key_path + ".x509.pem",
+      "--key", args.apk_key_path + ".pk8"])
diff --git a/tools/releasetools/fsverity_metadata_generator.py b/tools/releasetools/fsverity_metadata_generator.py
index a300d2e..fa7cd39 100644
--- a/tools/releasetools/fsverity_metadata_generator.py
+++ b/tools/releasetools/fsverity_metadata_generator.py
@@ -178,6 +178,7 @@
           out.write(sig)
       else:
         out.write(pack('<I', SIG_TYPE_NONE))
+        out.write(pack('<I', 0))
 
       # 4. merkle tree
       with open(merkletree_file, 'rb') as f:
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index c21de14..58f0e85 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -230,6 +230,9 @@
 
   --compressor_types
       A colon ':' separated list of compressors. Allowed values are bz2 and brotli.
+
+  --enable_zucchini
+      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory.
 """
 
 from __future__ import print_function
@@ -299,6 +302,7 @@
 OPTIONS.enable_vabc_xor = True
 OPTIONS.force_minor_version = None
 OPTIONS.compressor_types = None
+OPTIONS.enable_zucchini = None
 
 POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
 DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
@@ -1141,6 +1145,14 @@
     partition_timestamps_flags = GeneratePartitionTimestampFlags(
         metadata.postcondition.partition_state)
 
+  # Auto-check for compatibility only if --enable_zucchini omitted. Otherwise
+  # let user override zucchini settings. This is useful for testing.
+  if OPTIONS.enable_zucchini is None:
+    if not ota_utils.IsZucchiniCompatible(source_file, target_file):
+      additional_args += ["--enable_zucchini", "false"]
+  else:
+    additional_args += ["--enable_zucchini", str(OPTIONS.enable_zucchini).lower()]
+
   if OPTIONS.disable_vabc:
     additional_args += ["--disable_vabc", "true"]
   if OPTIONS.enable_vabc_xor:
@@ -1326,6 +1338,8 @@
       OPTIONS.force_minor_version = a
     elif o == "--compressor_types":
       OPTIONS.compressor_types = a
+    elif o == "--enable_zucchini":
+      OPTIONS.enable_zucchini = a.lower() != "false"
     else:
       return False
     return True
@@ -1373,6 +1387,7 @@
                                  "enable_vabc_xor=",
                                  "force_minor_version=",
                                  "compressor_types=",
+                                 "enable_zucchin=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 6c5fc05..a4ec9e2 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -638,3 +638,38 @@
       target_apex.source_version = source_apex_versions[name]
 
   return target_apex_proto.SerializeToString()
+
+
+def IsZucchiniCompatible(source_file: str, target_file: str):
+  """Check whether zucchini versions in two builds are compatible
+
+  Args:
+    source_file: Path to source build's target_file.zip
+    target_file: Path to target build's target_file.zip
+
+  Returns:
+    bool true if and only if zucchini versions are compatible
+  """
+  if source_file is None or target_file is None:
+    return False
+  assert os.path.exists(source_file)
+  assert os.path.exists(target_file)
+
+  assert zipfile.is_zipfile(source_file) or os.path.isdir(source_file)
+  assert zipfile.is_zipfile(target_file) or os.path.isdir(target_file)
+  _ZUCCHINI_CONFIG_ENTRY_NAME = "META/zucchini_config.txt"
+
+  def ReadEntry(path, entry):
+    # Read an entry inside a .zip file or extracted dir of .zip file
+    if zipfile.is_zipfile(path):
+      with zipfile.ZipFile(path, "r", allowZip64=True) as zfp:
+        if entry in zfp.namelist():
+          return zfp.read(entry).decode()
+    else:
+      entry_path = os.path.join(entry, path)
+      if os.path.exists(entry_path):
+        with open(entry_path, "r") as fp:
+          return fp.read()
+      else:
+        return ""
+  return ReadEntry(source_file, _ZUCCHINI_CONFIG_ENTRY_NAME) == ReadEntry(target_file, _ZUCCHINI_CONFIG_ENTRY_NAME)