Merge "Make implicit 32-bit apps on 64-bit target an error"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index a93e79e..1d0685d 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -632,6 +632,8 @@
 $(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/BootSignature.jar)
 $(call add-clean-step, rm -rf $(HOST_OUT_JAVA_LIBRARIES)/VeritySigner.jar)
 $(call add-clean-step, rm -rf $(HOST_OUT_EXECUTABLES)/build_verity_metadata.py)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libc_malloc*)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/Deprecation.md b/Deprecation.md
index 01825b2..0d925cb 100644
--- a/Deprecation.md
+++ b/Deprecation.md
@@ -12,11 +12,20 @@
 
 [build/make/core/deprecation.mk] is the source of truth, but for easy browsing:
 
-| Module type                | State     |
-| -------------------------- | --------- |
-| `BUILD_HOST_TEST_CONFIG`   | Error     |
-| `BUILD_TARGET_TEST_CONFIG` | Error     |
-| `BUILD_*`                  | Available |
+| Module type                      | State     |
+| -------------------------------- | --------- |
+| `BUILD_AUX_EXECUTABLE`           | Warning   |
+| `BUILD_AUX_STATIC_LIBRARY`       | Warning   |
+| `BUILD_HOST_FUZZ_TEST`           | Warning   |
+| `BUILD_HOST_NATIVE_TEST`         | Warning   |
+| `BUILD_HOST_SHARED_TEST_LIBRARY` | Error     |
+| `BUILD_HOST_STATIC_TEST_LIBRARY` | Warning   |
+| `BUILD_HOST_TEST_CONFIG`         | Error     |
+| `BUILD_NATIVE_BENCHMARK`         | Warning   |
+| `BUILD_SHARED_TEST_LIBRARY`      | Error     |
+| `BUILD_STATIC_TEST_LIBRARY`      | Warning   |
+| `BUILD_TARGET_TEST_CONFIG`       | Error     |
+| `BUILD_*`                        | Available |
 
 ## Module Type Deprecation Process
 
diff --git a/core/Makefile b/core/Makefile
index a2ef3fd..bbd7426 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -459,10 +459,11 @@
 
 build_desc :=
 
-ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
-INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
-else
 INSTALLED_RECOVERYIMAGE_TARGET :=
+ifdef BUILDING_RECOVERY_IMAGE
+ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
+endif
 endif
 
 $(INSTALLED_BUILD_PROP_TARGET): $(intermediate_system_build_prop) $(INSTALLED_RECOVERYIMAGE_TARGET)
@@ -487,6 +488,12 @@
 	@echo Target vendor buildinfo: $@
 	@mkdir -p $(dir $@)
 	$(hide) echo > $@
+ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
+	$(hide) echo ro.boot.dynamic_partitions=true >> $@
+endif
+ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
+	$(hide) echo ro.boot.dynamic_partitions_retrofit=true >> $@
+endif
 	$(hide) grep 'ro.product.first_api_level' $(intermediate_system_build_prop) >> $@ || true
 	$(hide) echo ro.vendor.build.security_patch="$(VENDOR_SECURITY_PATCH)">>$@
 	$(hide) echo ro.vendor.product.cpu.abilist="$(TARGET_CPU_ABI_LIST)">>$@
@@ -900,13 +907,14 @@
 
 endif # BUILDING_RAMDISK_IMAGE
 
-
-INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
-
-ifneq ($(strip $(TARGET_NO_KERNEL)),true)
-
 # -----------------------------------------------------------------
 # the boot image, which is a collection of other images.
+
+# This is defined here since we may be building recovery as boot
+# below and only want to define this once
+BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
+
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
 INTERNAL_BOOTIMAGE_ARGS := \
 	$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
 	--kernel $(INSTALLED_KERNEL_TARGET)
@@ -945,8 +953,10 @@
     --os_version $(PLATFORM_VERSION) \
     --os_patch_level $(PLATFORM_SECURITY_PATCH)
 
-# We build recovery as boot image if BOARD_USES_RECOVERY_AS_BOOT is true.
-ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+# Define these only if we are building boot
+ifdef BUILDING_BOOT_IMAGE
+INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)
+
 ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
 $(error TARGET_BOOTIMAGE_USE_EXT2 is not supported anymore)
 
@@ -1017,7 +1027,7 @@
 	$(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
 
 endif # TARGET_BOOTIMAGE_USE_EXT2
-endif # BOARD_USES_RECOVERY_AS_BOOT
+endif # BUILDING_BOOT_IMAGE
 
 else # TARGET_NO_KERNEL == "true"
 ifdef BOARD_PREBUILT_BOOTIMAGE
@@ -1493,7 +1503,7 @@
 # Recovery image
 
 # Recovery image exists if we are building recovery, or building recovery as boot.
-ifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))
+ifdef BUILDING_RECOVERY_IMAGE
 
 INTERNAL_RECOVERYIMAGE_FILES := $(filter $(TARGET_RECOVERY_OUT)/%, \
     $(ALL_DEFAULT_INSTALLED_MODULES))
@@ -1506,6 +1516,7 @@
 # build-recoveryimage-target, which would touch the files under TARGET_RECOVERY_OUT and race with
 # the call to FILELIST.
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)
 $(INSTALLED_FILES_FILE_RECOVERY): $(INSTALLED_BOOTIMAGE_TARGET)
 else
 $(INSTALLED_FILES_FILE_RECOVERY): $(INSTALLED_RECOVERYIMAGE_TARGET)
@@ -1924,9 +1935,9 @@
 	@echo "make $@: ignoring dependencies"
 	$(call build-recoveryimage-target, $(INSTALLED_RECOVERYIMAGE_TARGET))
 
-else # INSTALLED_RECOVERYIMAGE_TARGET not defined
+else # BUILDING_RECOVERY_IMAGE
 RECOVERY_RESOURCE_ZIP :=
-endif
+endif # BUILDING_RECOVERY_IMAGE
 
 .PHONY: recoveryimage
 recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP)
@@ -2030,17 +2041,40 @@
 INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INSTALLED_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_BOOTIMAGE_ARGS))
 endif
 
+# If boot.img is chained but boot-debug.img is not signed, libavb in bootloader
+# will fail to find valid AVB metadata from the end of /boot, thus stop booting.
+# Using a test key to sign boot-debug.img to continue booting with the mismatched
+# public key, if the device is unlocked.
+ifneq ($(BOARD_AVB_BOOT_KEY_PATH),)
+BOARD_AVB_DEBUG_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS := \
+  --algorithm SHA256_RSA2048 --key $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
+endif
+
 # Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img
 $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
 	$(call pretty,"Target boot debug image: $@")
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
-	$(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
+	  $(call assert-max-image-size,$@,$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
+	  $(AVBTOOL) add_hash_footer \
+	    --image $@ \
+	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS), \
+	  $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
 
 .PHONY: bootimage_debug-nodeps
 bootimage_debug-nodeps: $(MKBOOTIMG)
 	echo "make $@: ignoring dependencies"
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
-	$(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
+	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
+	  $(AVBTOOL) add_hash_footer \
+	    --image $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
+	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS), \
+	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
 
 endif # TARGET_NO_KERNEL
 
@@ -3459,134 +3493,112 @@
 endif
 
 ifeq ($(build_otatools_package),true)
-OTATOOLS :=  $(HOST_OUT_EXECUTABLES)/minigzip \
-  $(HOST_OUT_EXECUTABLES)/aapt \
-  $(HOST_OUT_EXECUTABLES)/checkvintf \
-  $(HOST_OUT_EXECUTABLES)/mkbootfs \
-  $(HOST_OUT_EXECUTABLES)/mkbootimg \
-  $(HOST_OUT_EXECUTABLES)/fs_config \
-  $(HOST_OUT_EXECUTABLES)/zipalign \
-  $(HOST_OUT_EXECUTABLES)/bsdiff \
-  $(HOST_OUT_EXECUTABLES)/imgdiff \
-  $(HOST_OUT_JAVA_LIBRARIES)/signapk.jar \
-  $(HOST_OUT_JAVA_LIBRARIES)/boot_signer.jar \
-  $(HOST_OUT_JAVA_LIBRARIES)/verity_signer.jar \
-  $(HOST_OUT_EXECUTABLES)/mke2fs \
-  $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs \
-  $(HOST_OUT_EXECUTABLES)/e2fsdroid \
-  $(HOST_OUT_EXECUTABLES)/tune2fs \
-  $(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh \
-  $(HOST_OUT_EXECUTABLES)/mksquashfs \
-  $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh \
-  $(HOST_OUT_EXECUTABLES)/make_f2fs \
-  $(HOST_OUT_EXECUTABLES)/sload_f2fs \
-  $(HOST_OUT_EXECUTABLES)/simg2img \
-  $(HOST_OUT_EXECUTABLES)/e2fsck \
-  $(HOST_OUT_EXECUTABLES)/generate_verity_key \
-  $(HOST_OUT_EXECUTABLES)/verity_signer \
-  $(HOST_OUT_EXECUTABLES)/verity_verifier \
-  $(HOST_OUT_EXECUTABLES)/append2simg \
-  $(HOST_OUT_EXECUTABLES)/img2simg \
-  $(HOST_OUT_EXECUTABLES)/boot_signer \
-  $(HOST_OUT_EXECUTABLES)/fec \
-  $(HOST_OUT_EXECUTABLES)/brillo_update_payload \
-  $(HOST_OUT_EXECUTABLES)/lib/shflags/shflags \
-  $(HOST_OUT_EXECUTABLES)/delta_generator \
-  $(HOST_OUT_EXECUTABLES)/care_map_generator \
-  $(HOST_OUT_EXECUTABLES)/fc_sort \
-  $(HOST_OUT_EXECUTABLES)/sefcontext_compile \
-  $(LPMAKE) \
-  $(AVBTOOL) \
-  $(BLK_ALLOC_TO_BASE_FS) \
-  $(BROTLI) \
-  $(BUILD_VERITY_METADATA) \
-  $(BUILD_VERITY_TREE)
+
+INTERNAL_OTATOOLS_MODULES := \
+  aapt \
+  append2simg \
+  avbtool \
+  blk_alloc_to_base_fs \
+  boot_signer \
+  brillo_update_payload \
+  brotli \
+  bsdiff \
+  build_verity_metadata \
+  build_verity_tree \
+  care_map_generator \
+  checkvintf \
+  delta_generator \
+  e2fsck \
+  e2fsdroid \
+  fc_sort \
+  fec \
+  fs_config \
+  generate_verity_key \
+  img2simg \
+  imgdiff \
+  libconscrypt_openjdk_jni \
+  lpmake \
+  make_f2fs \
+  minigzip \
+  mkbootfs \
+  mkbootimg \
+  mke2fs \
+  mke2fs.conf \
+  mkf2fsuserimg.sh \
+  mksquashfs \
+  mksquashfsimage.sh \
+  mkuserimg_mke2fs \
+  sefcontext_compile \
+  shflags \
+  signapk \
+  simg2img \
+  sload_f2fs \
+  tune2fs \
+  verity_signer \
+  verity_verifier \
+  zipalign \
 
 ifeq (true,$(PRODUCT_SUPPORTS_VBOOT))
-OTATOOLS += \
-  $(FUTILITY) \
-  $(VBOOT_SIGNER)
+INTERNAL_OTATOOLS_MODULES += \
+  futility \
+  vboot_signer
 endif
 
-# Shared libraries.
-OTATOOLS += \
-  $(HOST_LIBRARY_PATH)/libc++$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/liblog$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libcutils$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libselinux$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libcrypto_utils$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libcrypto-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2fs-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_blkid-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_com_err-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_e2p-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_misc$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_profile-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_quota-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext2_uuid-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libconscrypt_openjdk_jni$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbrillo$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbrillo-stream$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libchrome$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libcurl-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libevent-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libprotobuf-cpp-lite$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libssl-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libz-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libsparse-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbase$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libpcre2$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbrotli$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/liblp$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libext4_utils$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libfec$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libsquashfs_utils$(HOST_SHLIB_SUFFIX)
-
+INTERNAL_OTATOOLS_FILES := \
+  $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES)))
 
 .PHONY: otatools
-otatools: $(OTATOOLS)
+otatools: $(INTERNAL_OTATOOLS_FILES)
 
-BUILT_OTATOOLS_PACKAGE := $(PRODUCT_OUT)/otatools.zip
-$(BUILT_OTATOOLS_PACKAGE): zip_root := $(call intermediates-dir-for,PACKAGING,otatools)/otatools
+# For each module, recursively resolve its host shared library dependencies. Then we have a full
+# list of modules whose installed files need to be packed.
+INTERNAL_OTATOOLS_MODULES_WITH_DEPS := \
+  $(sort $(INTERNAL_OTATOOLS_MODULES) \
+      $(foreach m,$(INTERNAL_OTATOOLS_MODULES),$(call get-all-shared-libs-deps,$(m))))
 
-OTATOOLS_DEPS := \
-  system/extras/ext4_utils/mke2fs.conf \
-  $(sort $(shell find build/make/target/product/security -type f -name "*.x509.pem" -o -name "*.pk8" -o \
-      -name verity_key))
+INTERNAL_OTATOOLS_PACKAGE_FILES := \
+  $(filter $(HOST_OUT)/%,$(call module-installed-files,$(INTERNAL_OTATOOLS_MODULES_WITH_DEPS)))
+
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
+  $(sort $(shell find build/make/target/product/security -type f -name "*.x509.pem" -o \
+      -name "*.pk8" -o -name verity_key))
 
 ifneq (,$(wildcard device))
-OTATOOLS_DEPS += \
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
   $(sort $(shell find device $(wildcard vendor) -type f -name "*.pk8" -o -name "verifiedboot*" -o \
       -name "*.x509.pem" -o -name "oem*.prop"))
 endif
 ifneq (,$(wildcard external/avb))
-OTATOOLS_DEPS += \
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
   $(sort $(shell find external/avb/test/data -type f -name "testkey_*.pem" -o \
       -name "atx_metadata.bin"))
 endif
 ifneq (,$(wildcard system/update_engine))
-OTATOOLS_DEPS += \
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
   $(sort $(shell find system/update_engine/scripts -name "*.pyc" -prune -o -type f -print))
 endif
-
-OTATOOLS_RELEASETOOLS := \
-  $(sort $(shell find build/make/tools/releasetools -name "*.pyc" -prune -o -type f))
-
 ifeq (true,$(PRODUCT_SUPPORTS_VBOOT))
-OTATOOLS_DEPS += \
+INTERNAL_OTATOOLS_PACKAGE_FILES += \
   $(sort $(shell find external/vboot_reference/tests/devkeys -type f))
 endif
 
-$(BUILT_OTATOOLS_PACKAGE): $(OTATOOLS) $(OTATOOLS_DEPS) $(OTATOOLS_RELEASETOOLS) $(SOONG_ZIP)
+INTERNAL_OTATOOLS_RELEASETOOLS := \
+  $(sort $(shell find build/make/tools/releasetools -name "*.pyc" -prune -o \
+      \( -type f -o -type l \) -print))
+
+BUILT_OTATOOLS_PACKAGE := $(PRODUCT_OUT)/otatools.zip
+$(BUILT_OTATOOLS_PACKAGE): PRIVATE_ZIP_ROOT := $(call intermediates-dir-for,PACKAGING,otatools)/otatools
+$(BUILT_OTATOOLS_PACKAGE): PRIVATE_OTATOOLS_PACKAGE_FILES := $(INTERNAL_OTATOOLS_PACKAGE_FILES)
+$(BUILT_OTATOOLS_PACKAGE): PRIVATE_OTATOOLS_RELEASETOOLS := $(INTERNAL_OTATOOLS_RELEASETOOLS)
+$(BUILT_OTATOOLS_PACKAGE): $(INTERNAL_OTATOOLS_PACKAGE_FILES) $(INTERNAL_OTATOOLS_RELEASETOOLS)
+$(BUILT_OTATOOLS_PACKAGE): $(SOONG_ZIP)
 	@echo "Package OTA tools: $@"
-	$(hide) rm -rf $@ $(zip_root)
-	$(hide) mkdir -p $(dir $@) $(zip_root)/bin $(zip_root)/framework $(zip_root)/releasetools
-	$(call copy-files-with-structure,$(OTATOOLS),$(HOST_OUT)/,$(zip_root))
-	$(hide) cp $(SOONG_ZIP) $(zip_root)/bin/
-	$(hide) cp -r -d -p build/make/tools/releasetools/* $(zip_root)/releasetools
-	$(hide) rm -rf $@ $(zip_root)/releasetools/*.pyc
-	$(hide) $(SOONG_ZIP) -o $@ -C $(zip_root) -D $(zip_root) \
-	  -C . $(addprefix -f ,$(OTATOOLS_DEPS))
+	rm -rf $@ $(PRIVATE_ZIP_ROOT)
+	mkdir -p $(dir $@)
+	$(call copy-files-with-structure,$(PRIVATE_OTATOOLS_PACKAGE_FILES),$(HOST_OUT)/,$(PRIVATE_ZIP_ROOT))
+	$(call copy-files-with-structure,$(PRIVATE_OTATOOLS_RELEASETOOLS),build/make/tools/,$(PRIVATE_ZIP_ROOT))
+	cp $(SOONG_ZIP) $(PRIVATE_ZIP_ROOT)/bin/
+	$(SOONG_ZIP) -o $@ -C $(PRIVATE_ZIP_ROOT) -D $(PRIVATE_ZIP_ROOT)
 
 .PHONY: otatools-package
 otatools-package: $(BUILT_OTATOOLS_PACKAGE)
@@ -3888,6 +3900,9 @@
 ifdef BOARD_BOOTIMAGE_PARTITION_SIZE
 	$(hide) echo "boot_size=$(BOARD_BOOTIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
 endif
+ifeq ($(INSTALLED_BOOTIMAGE_TARGET),)
+	$(hide) echo "no_boot=true" >> $(zip_root)/META/misc_info.txt
+endif
 ifeq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
 	$(hide) echo "no_recovery=true" >> $(zip_root)/META/misc_info.txt
 endif
@@ -4264,7 +4279,7 @@
 	$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)
 	$(hide) mkdir -p $(dir $@) $(TARGET_OUT_UNSTRIPPED) $(dir $(PRIVATE_LIST_FILE))
 	$(hide) find -L $(TARGET_OUT_UNSTRIPPED) -type f | sort >$(PRIVATE_LIST_FILE)
-	$(hide) $(SOONG_ZIP) -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE)
+	$(hide) $(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE)
 # -----------------------------------------------------------------
 # A zip of the coverage directory.
 #
diff --git a/core/binary.mk b/core/binary.mk
index f3a50f1..a420c02 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -1402,9 +1402,6 @@
 $(notice_target): | $(installed_static_library_notice_file_targets)
 $(LOCAL_INSTALLED_MODULE): | $(notice_target)
 
-$(notice_target): | $(installed_static_library_notice_file_targets)
-$(LOCAL_INSTALLED_MODULE): | $(notice_target)
-
 # Default is -fno-rtti.
 ifeq ($(strip $(LOCAL_RTTI_FLAG)),)
 LOCAL_RTTI_FLAG := -fno-rtti
diff --git a/core/board_config.mk b/core/board_config.mk
index 7e55bfc..d83fa32 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -290,8 +290,33 @@
 endif
 .KATI_READONLY := BUILDING_CACHE_IMAGE
 
-# TODO: Add BUILDING_BOOT_IMAGE / BUILDING_RECOVERY_IMAGE
-# This gets complicated with BOARD_USES_RECOVERY_AS_BOOT, so skipping for now.
+# Are we building a boot image
+BUILDING_BOOT_IMAGE :=
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+  BUILDING_BOOT_IMAGE :=
+else ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),)
+  ifdef BOARD_BOOTIMAGE_PARTITION_SIZE
+    BUILDING_BOOT_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),true)
+  BUILDING_BOOT_IMAGE := true
+endif
+.KATI_READONLY := BUILDING_BOOT_IMAGE
+
+# Are we building a recovery image
+BUILDING_RECOVERY_IMAGE :=
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+  BUILDING_RECOVERY_IMAGE := true
+else ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),)
+  ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE
+    ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
+      BUILDING_RECOVERY_IMAGE := true
+    endif
+  endif
+else ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),true)
+  BUILDING_RECOVERY_IMAGE := true
+endif
+.KATI_READONLY := BUILDING_RECOVERY_IMAGE
 
 # Are we building a ramdisk image
 BUILDING_RAMDISK_IMAGE := true
@@ -509,6 +534,14 @@
   TARGET_VENDOR_TEST_SUFFIX :=
 endif
 
+###########################################
+# APEXes are by default flattened, i.e. non-updatable.
+# It can be unflattened (and updatable) by inheriting from
+# updatable_apex.mk
+ifeq (,$(TARGET_FLATTEN_APEX))
+TARGET_FLATTEN_APEX := true
+endif
+
 ifeq (,$(TARGET_BUILD_APPS))
 ifdef PRODUCT_EXTRA_VNDK_VERSIONS
   $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v)))
diff --git a/core/config.mk b/core/config.mk
index bf59fb1..088bb15 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -608,7 +608,7 @@
 BUILD_VERITY_TREE := $(HOST_OUT_EXECUTABLES)/build_verity_tree
 BOOT_SIGNER := $(HOST_OUT_EXECUTABLES)/boot_signer
 FUTILITY := $(HOST_OUT_EXECUTABLES)/futility-host
-VBOOT_SIGNER := prebuilts/misc/scripts/vboot_signer/vboot_signer.sh
+VBOOT_SIGNER := $(HOST_OUT_EXECUTABLES)/vboot_signer
 FEC := $(HOST_OUT_EXECUTABLES)/fec
 BRILLO_UPDATE_PAYLOAD := $(HOST_OUT_EXECUTABLES)/brillo_update_payload
 
@@ -624,11 +624,9 @@
 
 USE_OPENJDK9 := true
 
-ifeq ($(EXPERIMENTAL_USE_OPENJDK9),)
+ifeq ($(EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9),)
 TARGET_OPENJDK9 :=
-else ifeq ($(EXPERIMENTAL_USE_OPENJDK9),1.8)
-TARGET_OPENJDK9 :=
-else ifeq ($(EXPERIMENTAL_USE_OPENJDK9),true)
+else ifeq ($(EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9),true)
 TARGET_OPENJDK9 := true
 endif
 
@@ -815,15 +813,6 @@
     PLATFORM_SEPOLICY_VERSION \
     TOT_SEPOLICY_VERSION \
 
-ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),)
-  ifdef PRODUCT_SHIPPING_API_LEVEL
-    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29))
-      PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true
-    endif
-  endif
-endif
-.KATI_READONLY := PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
-
 ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
   ifneq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
     $(error PRODUCT_USE_DYNAMIC_PARTITIONS must be true when PRODUCT_RETROFIT_DYNAMIC_PARTITIONS \
diff --git a/core/deprecation.mk b/core/deprecation.mk
index 9d57527..11fe290 100644
--- a/core/deprecation.mk
+++ b/core/deprecation.mk
@@ -1,7 +1,5 @@
 # These module types can still be used without warnings or errors.
 AVAILABLE_BUILD_MODULE_TYPES :=$= \
-  BUILD_AUX_EXECUTABLE \
-  BUILD_AUX_STATIC_LIBRARY \
   BUILD_COPY_HEADERS \
   BUILD_EXECUTABLE \
   BUILD_FUZZ_TEST \
@@ -9,17 +7,12 @@
   BUILD_HOST_DALVIK_JAVA_LIBRARY \
   BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY \
   BUILD_HOST_EXECUTABLE \
-  BUILD_HOST_FUZZ_TEST \
   BUILD_HOST_JAVA_LIBRARY \
-  BUILD_HOST_NATIVE_TEST \
   BUILD_HOST_PREBUILT \
   BUILD_HOST_SHARED_LIBRARY \
-  BUILD_HOST_SHARED_TEST_LIBRARY \
   BUILD_HOST_STATIC_LIBRARY \
-  BUILD_HOST_STATIC_TEST_LIBRARY \
   BUILD_JAVA_LIBRARY \
   BUILD_MULTI_PREBUILT \
-  BUILD_NATIVE_BENCHMARK \
   BUILD_NATIVE_TEST \
   BUILD_NOTICE_FILE \
   BUILD_PACKAGE \
@@ -27,16 +20,21 @@
   BUILD_PREBUILT \
   BUILD_RRO_PACKAGE \
   BUILD_SHARED_LIBRARY \
-  BUILD_SHARED_TEST_LIBRARY \
   BUILD_STATIC_JAVA_LIBRARY \
   BUILD_STATIC_LIBRARY \
-  BUILD_STATIC_TEST_LIBRARY \
 
 # These are BUILD_* variables that will throw a warning when used. This is
 # generally a temporary state until all the devices are marked with the
 # relevant BUILD_BROKEN_USES_BUILD_* variables, then these would move to
 # DEFAULT_ERROR_BUILD_MODULE_TYPES.
 DEFAULT_WARNING_BUILD_MODULE_TYPES :=$= \
+  BUILD_AUX_EXECUTABLE \
+  BUILD_AUX_STATIC_LIBRARY \
+  BUILD_HOST_FUZZ_TEST \
+  BUILD_HOST_NATIVE_TEST \
+  BUILD_HOST_STATIC_TEST_LIBRARY \
+  BUILD_NATIVE_BENCHMARK \
+  BUILD_STATIC_TEST_LIBRARY \
 
 # These are BUILD_* variables that are errors to reference, but you can set
 # BUILD_BROKEN_USES_BUILD_* in your BoardConfig.mk in order to turn them back
@@ -48,6 +46,8 @@
 # These are BUILD_* variables that are always errors to reference.
 # Setting the BUILD_BROKEN_USES_BUILD_* variables is also an error.
 OBSOLETE_BUILD_MODULE_TYPES :=$= \
+  BUILD_HOST_SHARED_TEST_LIBRARY \
+  BUILD_SHARED_TEST_LIBRARY \
 
 $(foreach m,$(OBSOLETE_BUILD_MODULE_TYPES),\
   $(KATI_obsolete_var $(m),Please convert to Soong) \
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index b5834b0..c44e4bd 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -1,4 +1,4 @@
-DEX_PREOPT_CONFIG := $(PRODUCT_OUT)/dexpreopt.config
+DEX_PREOPT_CONFIG := $(SOONG_OUT_DIR)/dexpreopt.config
 
 # The default value for LOCAL_DEX_PREOPT
 DEX_PREOPT_DEFAULT ?= true
@@ -169,12 +169,6 @@
     fi)
 endif
 
-# Dummy rule to create dexpreopt.config, it will already have been created
-# by the $(file) call above, but a rule needs to exist to keep the dangling
-# rule check happy.
-$(DEX_PREOPT_CONFIG):
-	@#empty
-
 DEXPREOPT_GEN_DEPS := \
   $(SOONG_HOST_OUT_EXECUTABLES)/profman \
   $(DEX2OAT) \
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 85ddbfa..266ebd2 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -242,12 +242,12 @@
 
   .KATI_RESTAT: $(my_dexpreopt_script) $(my_strip_script)
   $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE)
-  $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(PRODUCT_OUT)/dexpreopt.config
+  $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE)
   $(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config)
   $(my_dexpreopt_script): PRIVATE_STRIP_SCRIPT := $(my_strip_script)
   $(my_dexpreopt_script): .KATI_IMPLICIT_OUTPUTS := $(my_strip_script)
   $(my_dexpreopt_script): $(DEXPREOPT_GEN)
-  $(my_dexpreopt_script): $(my_dexpreopt_config) $(PRODUCT_OUT)/dexpreopt.config
+  $(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_CONFIG_FOR_MAKE)
 	@echo "$(PRIVATE_MODULE) dexpreopt gen"
 	$(DEXPREOPT_GEN) -global $(PRIVATE_GLOBAL_CONFIG) -module $(PRIVATE_MODULE_CONFIG) \
 	-dexpreopt_script $@ -strip_script $(PRIVATE_STRIP_SCRIPT) \
diff --git a/core/host_static_test_lib.mk b/core/host_static_test_lib.mk
index a24cd62..a9e39b1 100644
--- a/core/host_static_test_lib.mk
+++ b/core/host_static_test_lib.mk
@@ -6,4 +6,4 @@
 
 include $(BUILD_SYSTEM)/host_test_internal.mk
 
-include $(BUILD_HOST_STATIC_LIBRARY)
+include $(BUILD_SYSTEM)/host_static_library.mk
diff --git a/core/main.mk b/core/main.mk
index 6f92ff1..df5c13c 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -243,14 +243,6 @@
 ADDITIONAL_DEFAULT_PROPERTIES += ro.actionable_compatible_property.enabled=${PRODUCT_COMPATIBLE_PROPERTY}
 endif
 
-ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
-ADDITIONAL_PRODUCT_PROPERTIES += ro.boot.dynamic_partitions=true
-endif
-
-ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
-ADDITIONAL_PRODUCT_PROPERTIES += ro.boot.dynamic_partitions_retrofit=true
-endif
-
 # Add the system server compiler filter if they are specified for the product.
 ifneq (,$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))
 ADDITIONAL_PRODUCT_PROPERTIES += dalvik.vm.systemservercompilerfilter=$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER)
@@ -1208,31 +1200,44 @@
 # Runtime APEX libraries
 APEX_MODULE_LIBS := \
   libadbconnection.so \
+  libadbconnectiond.so \
   libandroidicu.so \
   libandroidio.so \
   libart-compiler.so \
   libart-dexlayout.so \
+  libart-disassembler.so \
   libart.so \
   libartbase.so \
+  libartbased.so \
+  libartd-compiler.so \
+  libartd-dexlayout.so \
+  libartd.so \
   libartpalette.so \
+  libc.so \
   libdexfile.so \
   libdexfile_external.so \
+  libdexfiled.so \
   libdexfiled_external.so \
+  libdl.so \
   libdt_fd_forward.so \
   libdt_socket.so \
   libicui18n.so \
   libicuuc.so \
   libjavacore.so \
   libjdwp.so \
+  libm.so \
   libnativebridge.so \
   libnativehelper.so \
   libnativeloader.so \
   libnpt.so \
   libopenjdk.so \
   libopenjdkjvm.so \
+  libopenjdkjvmd.so \
   libopenjdkjvmti.so \
+  libopenjdkjvmtid.so \
   libpac.so \
   libprofile.so \
+  libprofiled.so \
   libsigchain.so \
 
 # Conscrypt APEX libraries
@@ -1243,6 +1248,9 @@
 # still may create these libraries in /system (b/129006418).
 DISABLE_APEX_LIBS_ABSENCE_CHECK ?=
 
+# Bionic should not be in /system, except for the bootstrap instance.
+APEX_LIBS_ABSENCE_CHECK_EXCLUDE := lib/bootstrap lib64/bootstrap
+
 # Exclude lib/arm and lib/arm64 which contain the native bridge proxy libs. They
 # are compiled for the guest architecture and used with an entirely different
 # linker config. The native libs are then linked to as usual via exported
@@ -1250,7 +1258,7 @@
 # native architecture.
 # TODO(b/130630776): Introduce a make variable for the appropriate directory
 # when native bridge is active.
-APEX_LIBS_ABSENCE_CHECK_EXCLUDE := lib/arm lib/arm64
+APEX_LIBS_ABSENCE_CHECK_EXCLUDE += lib/arm lib/arm64
 
 # Exclude vndk-* subdirectories which contain prebuilts from older releases.
 APEX_LIBS_ABSENCE_CHECK_EXCLUDE += lib/vndk-% lib64/vndk-%
diff --git a/core/node_fns.mk b/core/node_fns.mk
index ccfcc25..b81d60c 100644
--- a/core/node_fns.mk
+++ b/core/node_fns.mk
@@ -142,7 +142,8 @@
 #
 # $(1): context prefix
 # $(2): name of this node
-# $(3): list of variable names
+# $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 define _expand-inherited-values
   $(foreach v,$(3), \
@@ -154,15 +155,21 @@
             $(patsubst $(INHERIT_TAG)%,%, \
                 $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \
      )))) \
+    $(eval ### "Whether this variable should only take a single value") \
+    $(eval _eiv_sv := $(filter $(v),$(4))) \
     $(foreach i,$(_eiv_i), \
       $(eval ### "Make sure that this inherit appears only once") \
       $(eval $(_eiv_tv) := \
           $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \
+      $(eval ### "The expanded value, empty if we want a single value and have one") \
+      $(eval _eiv_ev := \
+        $(if $(and $(_eiv_sv),$(filter-out $(INHERIT_TAG)%,$($(_eiv_tv)))),,\
+          $($(1).$(i).$(v)) \
+        ) \
+      ) \
       $(eval ### "Expand the inherit tag") \
       $(eval $(_eiv_tv) := \
-          $(strip \
-              $(patsubst $(INHERIT_TAG)$(i),$($(1).$(i).$(v)), \
-                  $($(_eiv_tv))))) \
+          $(strip $(patsubst $(INHERIT_TAG)$(i),$(_eiv_ev),$($(_eiv_tv))))) \
       $(eval ### "Clear the child so DAGs don't create duplicate entries" ) \
       $(eval $(1).$(i).$(v) :=) \
       $(eval ### "If we just inherited ourselves, it's a cycle.") \
@@ -180,6 +187,7 @@
 # $(1): context prefix
 # $(2): makefile representing this node
 # $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 # _include_stack contains the list of included files, with the most recent files first.
 define _import-node
@@ -198,7 +206,7 @@
       $(call get-inherited-nodes,$(1).$(2),$(3)))
   $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3))
 
-  $(call _expand-inherited-values,$(1),$(2),$(3))
+  $(call _expand-inherited-values,$(1),$(2),$(3),$(4))
 
   $(eval $(1).$(2).inherited :=)
   $(eval _include_stack := $(wordlist 2,9999,$$(_include_stack)))
@@ -215,6 +223,7 @@
 # $(1): context prefix
 # $(2): list of makefiles representing nodes to import
 # $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 #TODO: Make the "does not exist" message more helpful;
 #      should print out the name of the file trying to include it.
@@ -225,7 +234,7 @@
         $(eval ### "skipping already-imported $(_in)") \
        , \
         $(eval $(1).$(_in).seen := true) \
-        $(call _import-node,$(1),$(strip $(_in)),$(3)) \
+        $(call _import-node,$(1),$(strip $(_in)),$(3),$(4)) \
        ) \
      , \
       $(error $(1): "$(_in)" does not exist) \
@@ -237,6 +246,8 @@
 # $(1): output list variable name, like "PRODUCTS" or "DEVICES"
 # $(2): list of makefiles representing nodes to import
 # $(3): list of node variable names
+# $(4): list with subset of variable names that take only a single value, instead
+#       of the default list semantics
 #
 define import-nodes
 $(if \
@@ -245,7 +256,7 @@
     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \
                 should be empty here: $(_include_stack))),) \
     $(eval _include_stack := ) \
-    $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \
+    $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3),$(4)) \
     $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \
     $(eval _node_import_context :=) \
     $(eval $(1) := $($(1)) $(_in)) \
diff --git a/core/product.mk b/core/product.mk
index 66570e4..838673c 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -106,30 +106,37 @@
 $(call get-product-makefiles,$(_find-android-products-files))
 endef
 
-_product_var_list :=
-_product_var_list += PRODUCT_NAME
-_product_var_list += PRODUCT_MODEL
+# Variables that are meant to hold only a single value.
+# - The value set in the current makefile takes precedence over inherited values
+# - If multiple inherited makefiles set the var, the first-inherited value wins
+_product_single_value_vars :=
+
+# Variables that are lists of values.
+_product_list_vars :=
+
+_product_single_value_vars += PRODUCT_NAME
+_product_single_value_vars += PRODUCT_MODEL
 
 # The resoure configuration options to use for this product.
-_product_var_list += PRODUCT_LOCALES
-_product_var_list += PRODUCT_AAPT_CONFIG
-_product_var_list += PRODUCT_AAPT_PREF_CONFIG
-_product_var_list += PRODUCT_AAPT_PREBUILT_DPI
-_product_var_list += PRODUCT_HOST_PACKAGES
-_product_var_list += PRODUCT_PACKAGES
-_product_var_list += PRODUCT_PACKAGES_DEBUG
-_product_var_list += PRODUCT_PACKAGES_DEBUG_ASAN
-_product_var_list += PRODUCT_PACKAGES_ENG
-_product_var_list += PRODUCT_PACKAGES_TESTS
+_product_list_vars += PRODUCT_LOCALES
+_product_list_vars += PRODUCT_AAPT_CONFIG
+_product_list_vars += PRODUCT_AAPT_PREF_CONFIG
+_product_list_vars += PRODUCT_AAPT_PREBUILT_DPI
+_product_list_vars += PRODUCT_HOST_PACKAGES
+_product_list_vars += PRODUCT_PACKAGES
+_product_list_vars += PRODUCT_PACKAGES_DEBUG
+_product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN
+_product_list_vars += PRODUCT_PACKAGES_ENG
+_product_list_vars += PRODUCT_PACKAGES_TESTS
 
 # The device that this product maps to.
-_product_var_list += PRODUCT_DEVICE
-_product_var_list += PRODUCT_MANUFACTURER
-_product_var_list += PRODUCT_BRAND
+_product_single_value_vars += PRODUCT_DEVICE
+_product_single_value_vars += PRODUCT_MANUFACTURER
+_product_single_value_vars += PRODUCT_BRAND
 
 # These PRODUCT_SYSTEM_* flags, if defined, are used in place of the
 # corresponding PRODUCT_* flags for the sysprops on /system.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_SYSTEM_NAME \
     PRODUCT_SYSTEM_MODEL \
     PRODUCT_SYSTEM_DEVICE \
@@ -138,24 +145,26 @@
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
-_product_var_list += PRODUCT_PROPERTY_OVERRIDES
+_product_list_vars += PRODUCT_PROPERTY_OVERRIDES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to default.prop
-_product_var_list += PRODUCT_DEFAULT_PROPERTY_OVERRIDES
+_product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to build.prop of product partition
-_product_var_list += PRODUCT_PRODUCT_PROPERTIES
+_product_list_vars += PRODUCT_PRODUCT_PROPERTIES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to build.prop of product partition
-_product_var_list += PRODUCT_PRODUCT_SERVICES_PROPERTIES
-_product_var_list += PRODUCT_ODM_PROPERTIES
-_product_var_list += PRODUCT_CHARACTERISTICS
+_product_list_vars += PRODUCT_PRODUCT_SERVICES_PROPERTIES
+_product_list_vars += PRODUCT_ODM_PROPERTIES
+
+# The characteristics of the product, which among other things is passed to aapt
+_product_single_value_vars += PRODUCT_CHARACTERISTICS
 
 # A list of words like <source path>:<destination path>[:<owner>].
 # The file at the source path should be copied to the destination path
@@ -163,157 +172,163 @@
 # $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml".
 # The rules for these copy steps are defined in build/make/core/Makefile.
 # The optional :<owner> is used to indicate the owner of a vendor file.
-_product_var_list += PRODUCT_COPY_FILES
+_product_list_vars += PRODUCT_COPY_FILES
 
 # The OTA key(s) specified by the product config, if any.  The names
 # of these keys are stored in the target-files zip so that post-build
 # signing tools can substitute them for the test key embedded by
 # default.
-_product_var_list += PRODUCT_OTA_PUBLIC_KEYS
-_product_var_list += PRODUCT_EXTRA_RECOVERY_KEYS
+_product_list_vars += PRODUCT_OTA_PUBLIC_KEYS
+_product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS
 
 # Should we use the default resources or add any product specific overlays
-_product_var_list += PRODUCT_PACKAGE_OVERLAYS
-_product_var_list += DEVICE_PACKAGE_OVERLAYS
+_product_list_vars += PRODUCT_PACKAGE_OVERLAYS
+_product_list_vars += DEVICE_PACKAGE_OVERLAYS
 
 # Resource overlay list which must be excluded from enforcing RRO.
-_product_var_list += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS
+_product_list_vars += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS
 
 # Package list to apply enforcing RRO.
-_product_var_list += PRODUCT_ENFORCE_RRO_TARGETS
+_product_list_vars += PRODUCT_ENFORCE_RRO_TARGETS
 
-_product_var_list += PRODUCT_SDK_ATREE_FILES
-_product_var_list += PRODUCT_SDK_ADDON_NAME
-_product_var_list += PRODUCT_SDK_ADDON_COPY_FILES
-_product_var_list += PRODUCT_SDK_ADDON_COPY_MODULES
-_product_var_list += PRODUCT_SDK_ADDON_DOC_MODULES
-_product_var_list += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP
+_product_list_vars += PRODUCT_SDK_ATREE_FILES
+_product_list_vars += PRODUCT_SDK_ADDON_NAME
+_product_list_vars += PRODUCT_SDK_ADDON_COPY_FILES
+_product_list_vars += PRODUCT_SDK_ADDON_COPY_MODULES
+_product_list_vars += PRODUCT_SDK_ADDON_DOC_MODULES
+_product_list_vars += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP
 
 # which Soong namespaces to export to Make
-_product_var_list += PRODUCT_SOONG_NAMESPACES
+_product_list_vars += PRODUCT_SOONG_NAMESPACES
 
-_product_var_list += PRODUCT_DEFAULT_WIFI_CHANNELS
-_product_var_list += PRODUCT_DEFAULT_DEV_CERTIFICATE
-_product_var_list += PRODUCT_RESTRICT_VENDOR_FILES
+_product_list_vars += PRODUCT_DEFAULT_WIFI_CHANNELS
+_product_list_vars += PRODUCT_DEFAULT_DEV_CERTIFICATE
+_product_list_vars += PRODUCT_RESTRICT_VENDOR_FILES
 
 # The list of product-specific kernel header dirs
-_product_var_list += PRODUCT_VENDOR_KERNEL_HEADERS
+_product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS
 
 # A list of module names of BOOTCLASSPATH (jar files)
-_product_var_list += PRODUCT_BOOT_JARS
-_product_var_list += PRODUCT_SUPPORTS_BOOT_SIGNER
-_product_var_list += PRODUCT_SUPPORTS_VBOOT
-_product_var_list += PRODUCT_SUPPORTS_VERITY
-_product_var_list += PRODUCT_SUPPORTS_VERITY_FEC
-_product_var_list += PRODUCT_OEM_PROPERTIES
+_product_list_vars += PRODUCT_BOOT_JARS
+_product_list_vars += PRODUCT_SUPPORTS_BOOT_SIGNER
+_product_list_vars += PRODUCT_SUPPORTS_VBOOT
+_product_list_vars += PRODUCT_SUPPORTS_VERITY
+_product_list_vars += PRODUCT_SUPPORTS_VERITY_FEC
+_product_list_vars += PRODUCT_OEM_PROPERTIES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to default.prop of system partition
-_product_var_list += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
+_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
 
-_product_var_list += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
-_product_var_list += PRODUCT_VENDOR_PROPERTY_BLACKLIST
-_product_var_list += PRODUCT_SYSTEM_SERVER_APPS
-_product_var_list += PRODUCT_SYSTEM_SERVER_JARS
+_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
+_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST
+_product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
+_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS
 
 # All of the apps that we force preopt, this overrides WITH_DEXPREOPT.
-_product_var_list += PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK
-_product_var_list += PRODUCT_DEXPREOPT_SPEED_APPS
-_product_var_list += PRODUCT_LOADED_BY_PRIVILEGED_MODULES
-_product_var_list += PRODUCT_VBOOT_SIGNING_KEY
-_product_var_list += PRODUCT_VBOOT_SIGNING_SUBKEY
-_product_var_list += PRODUCT_VERITY_SIGNING_KEY
-_product_var_list += PRODUCT_SYSTEM_VERITY_PARTITION
-_product_var_list += PRODUCT_VENDOR_VERITY_PARTITION
-_product_var_list += PRODUCT_PRODUCT_VERITY_PARTITION
-_product_var_list += PRODUCT_PRODUCT_SERVICES_VERITY_PARTITION
-_product_var_list += PRODUCT_ODM_VERITY_PARTITION
-_product_var_list += PRODUCT_SYSTEM_SERVER_DEBUG_INFO
-_product_var_list += PRODUCT_OTHER_JAVA_DEBUG_INFO
+_product_list_vars += PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK
+_product_list_vars += PRODUCT_DEXPREOPT_SPEED_APPS
+_product_list_vars += PRODUCT_LOADED_BY_PRIVILEGED_MODULES
+_product_single_value_vars += PRODUCT_VBOOT_SIGNING_KEY
+_product_single_value_vars += PRODUCT_VBOOT_SIGNING_SUBKEY
+_product_single_value_vars += PRODUCT_VERITY_SIGNING_KEY
+_product_single_value_vars += PRODUCT_SYSTEM_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_VENDOR_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_PRODUCT_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_PRODUCT_SERVICES_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_ODM_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_SYSTEM_SERVER_DEBUG_INFO
+_product_single_value_vars += PRODUCT_OTHER_JAVA_DEBUG_INFO
 
 # Per-module dex-preopt configs.
-_product_var_list += PRODUCT_DEX_PREOPT_MODULE_CONFIGS
-_product_var_list += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
-_product_var_list += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
-_product_var_list += PRODUCT_DEX_PREOPT_BOOT_FLAGS
-_product_var_list += PRODUCT_DEX_PREOPT_PROFILE_DIR
-_product_var_list += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES
-_product_var_list += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING
-_product_var_list += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS
+_product_list_vars += PRODUCT_DEX_PREOPT_MODULE_CONFIGS
+_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
+_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
+_product_list_vars += PRODUCT_DEX_PREOPT_BOOT_FLAGS
+_product_list_vars += PRODUCT_DEX_PREOPT_PROFILE_DIR
+_product_list_vars += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES
+_product_list_vars += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING
+_product_list_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS
 
 # Boot image options.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \
     PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION \
     PRODUCT_USES_DEFAULT_ART_CONFIG \
 
-_product_var_list += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER
+_product_list_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER
 # Per-module sanitizer configs
-_product_var_list += PRODUCT_SANITIZER_MODULE_CONFIGS
-_product_var_list += PRODUCT_SYSTEM_BASE_FS_PATH
-_product_var_list += PRODUCT_VENDOR_BASE_FS_PATH
-_product_var_list += PRODUCT_PRODUCT_BASE_FS_PATH
-_product_var_list += PRODUCT_PRODUCT_SERVICES_BASE_FS_PATH
-_product_var_list += PRODUCT_ODM_BASE_FS_PATH
-_product_var_list += PRODUCT_SHIPPING_API_LEVEL
-_product_var_list += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES
-_product_var_list += VENDOR_EXCEPTION_MODULES
-_product_var_list += VENDOR_EXCEPTION_PATHS
+_product_list_vars += PRODUCT_SANITIZER_MODULE_CONFIGS
+_product_single_value_vars += PRODUCT_SYSTEM_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_VENDOR_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_PRODUCT_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_PRODUCT_SERVICES_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_ODM_BASE_FS_PATH
 
+# The first API level this product shipped with
+_product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL
+
+_product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES
+_product_list_vars += VENDOR_EXCEPTION_MODULES
+_product_list_vars += VENDOR_EXCEPTION_PATHS
 # Whether the product wants to ship libartd. For rules and meaning, see art/Android.mk.
-_product_var_list += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD
+_product_single_value_vars += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD
 
 # Make this art variable visible to soong_config.mk.
-_product_var_list += PRODUCT_ART_USE_READ_BARRIER
+_product_single_value_vars += PRODUCT_ART_USE_READ_BARRIER
 
 # Whether the product is an Android Things variant.
-_product_var_list += PRODUCT_IOT
+_product_single_value_vars += PRODUCT_IOT
 
 # Add reserved headroom to a system image.
-_product_var_list += PRODUCT_SYSTEM_HEADROOM
+_product_single_value_vars += PRODUCT_SYSTEM_HEADROOM
 
 # Whether to save disk space by minimizing java debug info
-_product_var_list += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO
+_product_single_value_vars += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO
 
 # Whether any paths are excluded from sanitization when SANITIZE_TARGET=integer_overflow
-_product_var_list += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS
 
-_product_var_list += PRODUCT_ADB_KEYS
+_product_single_value_vars += PRODUCT_ADB_KEYS
 
 # Whether any paths should have CFI enabled for components
-_product_var_list += PRODUCT_CFI_INCLUDE_PATHS
+_product_list_vars += PRODUCT_CFI_INCLUDE_PATHS
 
 # Whether any paths are excluded from sanitization when SANITIZE_TARGET=cfi
-_product_var_list += PRODUCT_CFI_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_CFI_EXCLUDE_PATHS
 
 # Whether the Scudo hardened allocator is disabled platform-wide
-_product_var_list += PRODUCT_DISABLE_SCUDO
+_product_single_value_vars += PRODUCT_DISABLE_SCUDO
 
 # A flag to override PRODUCT_COMPATIBLE_PROPERTY
-_product_var_list += PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE
+_product_single_value_vars += PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE
+
+# List of extra VNDK versions to be included
+_product_list_vars += PRODUCT_EXTRA_VNDK_VERSIONS
 
 # Whether the whitelist of actionable compatible properties should be disabled or not
-_product_var_list += PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE
-_product_var_list += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
-_product_var_list += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT
-_product_var_list += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST
-_product_var_list += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT
-_product_var_list += PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST
+_product_single_value_vars += PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE
+
+_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
+_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT
+_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST
+_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT
+_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST
 
 # List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence
 # installed on /system directory by default.
-_product_var_list += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION
+_product_list_vars += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION
 
 # When this is true, dynamic partitions is retrofitted on a device that has
 # already been launched without dynamic partitions. Otherwise, the device
 # is launched with dynamic partitions.
 # This flag implies PRODUCT_USE_DYNAMIC_PARTITIONS.
-_product_var_list += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS
+_product_single_value_vars += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS
 
 # Other dynamic partition feature flags.PRODUCT_USE_DYNAMIC_PARTITION_SIZE and
 # PRODUCT_BUILD_SUPER_PARTITION default to the value of PRODUCT_USE_DYNAMIC_PARTITIONS.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_USE_DYNAMIC_PARTITIONS \
     PRODUCT_USE_DYNAMIC_PARTITION_SIZE \
     PRODUCT_BUILD_SUPER_PARTITION \
@@ -322,33 +337,40 @@
 # during OTA). Otherwise, kernel configuration requirements are enforced in VTS.
 # Devices that checks the running kernel (instead of the kernel in OTA package) should not
 # set this variable to prevent OTA failures.
-_product_var_list += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
+_product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
 
 # If set to true, this product builds a generic OTA package, which installs generic system images
 # onto matching devices. The product may only build a subset of system images (e.g. only
 # system.img), so devices need to install the package in a system-only OTA manner.
-_product_var_list += PRODUCT_BUILD_GENERIC_OTA_PACKAGE
+_product_single_value_vars += PRODUCT_BUILD_GENERIC_OTA_PACKAGE
 
 # Whether any paths are excluded from being set XOM when ENABLE_XOM=true
-_product_var_list += PRODUCT_XOM_EXCLUDE_PATHS
-_product_var_list += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
-_product_var_list += PRODUCT_PACKAGE_NAME_OVERRIDES
-_product_var_list += PRODUCT_CERTIFICATE_OVERRIDES
-_product_var_list += PRODUCT_BUILD_SYSTEM_IMAGE
-_product_var_list += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
-_product_var_list += PRODUCT_BUILD_VENDOR_IMAGE
-_product_var_list += PRODUCT_BUILD_PRODUCT_IMAGE
-_product_var_list += PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE
-_product_var_list += PRODUCT_BUILD_ODM_IMAGE
-_product_var_list += PRODUCT_BUILD_CACHE_IMAGE
-_product_var_list += PRODUCT_BUILD_RAMDISK_IMAGE
-_product_var_list += PRODUCT_BUILD_USERDATA_IMAGE
-_product_var_list += PRODUCT_UPDATABLE_BOOT_MODULES
-_product_var_list += PRODUCT_UPDATABLE_BOOT_LOCATIONS
+_product_list_vars += PRODUCT_XOM_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+_product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
+_product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
+
+# Controls for whether different partitions are built for the current product.
+_product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_VENDOR_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PRODUCT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_ODM_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_CACHE_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_RAMDISK_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE
+
+_product_list_vars += PRODUCT_UPDATABLE_BOOT_MODULES
+_product_list_vars += PRODUCT_UPDATABLE_BOOT_LOCATIONS
 
 # Whether the product would like to check prebuilt ELF files.
-_product_var_list += PRODUCT_CHECK_ELF_FILES
-.KATI_READONLY := _product_var_list
+_product_single_value_vars += PRODUCT_CHECK_ELF_FILES
+
+.KATI_READONLY := _product_single_value_vars _product_list_vars
+_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
 
 define dump-product
 $(warning ==== $(1) ====)\
@@ -422,7 +444,7 @@
 #
 #TODO: check to make sure that products have all the necessary vars defined
 define import-products
-$(call import-nodes,PRODUCTS,$(1),$(_product_var_list))
+$(call import-nodes,PRODUCTS,$(1),$(_product_var_list),$(_product_single_value_vars))
 endef
 
 
diff --git a/core/product_config.mk b/core/product_config.mk
index a088f06..5ba69a6 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -376,6 +376,14 @@
   PRODUCT_BUILD_SUPER_PARTITION := $(PRODUCT_USE_DYNAMIC_PARTITIONS)
 endif
 
+ifeq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),)
+  ifdef PRODUCT_SHIPPING_API_LEVEL
+    ifeq (true,$(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29))
+      PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true
+    endif
+  endif
+endif
+
 define product-overrides-config
 $$(foreach rule,$$(PRODUCT_$(1)_OVERRIDES),\
     $$(if $$(filter 2,$$(words $$(subst :,$$(space),$$(rule)))),,\
@@ -390,7 +398,6 @@
 
 # Macro to use below. $(1) is the name of the partition
 define product-build-image-config
-PRODUCT_BUILD_$(1)_IMAGE := $$(firstword $$(PRODUCT_BUILD_$(1)_IMAGE))
 ifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),)
     $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported)
 endif
@@ -406,7 +413,9 @@
     ODM \
     CACHE \
     RAMDISK \
-    USERDATA, \
+    USERDATA \
+    BOOT \
+    RECOVERY, \
   $(eval $(call product-build-image-config,$(image))))
 
 product-build-image-config :=
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_prebuilt.mk
index 31b0e63..7a8f46e 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_prebuilt.mk
@@ -223,7 +223,9 @@
 #
 # Filter out some NDK libraries that are not being exported.
 my_static_libraries := \
-    $(filter-out ndk_libc++_static ndk_libc++abi ndk_libandroid_support ndk_libunwind, \
+    $(filter-out ndk_libc++_static ndk_libc++abi ndk_libandroid_support ndk_libunwind \
+      ndk_libc++_static.native_bridge ndk_libc++abi.native_bridge \
+      ndk_libandroid_support.native_bridge ndk_libunwind.native_bridge, \
       $(LOCAL_STATIC_LIBRARIES))
 installed_static_library_notice_file_targets := \
     $(foreach lib,$(my_static_libraries) $(LOCAL_WHOLE_STATIC_LIBRARIES), \
diff --git a/core/tasks/check_boot_jars/package_whitelist.txt b/core/tasks/check_boot_jars/package_whitelist.txt
index b0becba..3b63843 100644
--- a/core/tasks/check_boot_jars/package_whitelist.txt
+++ b/core/tasks/check_boot_jars/package_whitelist.txt
@@ -46,6 +46,8 @@
 java\.util\.spi
 java\.util\.stream
 java\.util\.zip
+# TODO: Remove javax.annotation.processing if possible, see http://b/132338110:
+javax\.annotation\.processing
 javax\.crypto
 javax\.crypto\.interfaces
 javax\.crypto\.spec
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index 6c56671..be7c804 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -36,9 +36,6 @@
 
 BOARD_CHARGER_ENABLE_SUSPEND := true
 
-# Enable A/B update
-AB_OTA_UPDATER := true
-
 # Enable system property split for Treble
 BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true
 
diff --git a/target/board/mainline_arm64/BoardConfig.mk b/target/board/mainline_arm64/BoardConfig.mk
index 8bb6212..70505f4 100644
--- a/target/board/mainline_arm64/BoardConfig.mk
+++ b/target/board/mainline_arm64/BoardConfig.mk
@@ -28,6 +28,8 @@
 
 TARGET_NO_KERNEL := true
 
+# Build generic A/B format system-only OTA.
+AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS := system
 
 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 23289f5..606a605 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -122,8 +122,6 @@
     libcamera2ndk \
     libcamera_client \
     libcameraservice \
-    libc_malloc_debug \
-    libc_malloc_hooks \
     libcutils \
     libdl.bootstrap \
     libdrmframework \
diff --git a/target/product/mainline_system.mk b/target/product/mainline_system.mk
index 66207df..87393d4 100644
--- a/target/product/mainline_system.mk
+++ b/target/product/mainline_system.mk
@@ -19,7 +19,7 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
 # Enable updating of APEXes
-$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
+#$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
 # Add adb keys to debuggable AOSP builds (if they exist)
 $(call inherit-product-if-exists, vendor/google/security/adb/vendor_key.mk)
 
diff --git a/target/product/updatable_apex.mk b/target/product/updatable_apex.mk
index 4b31578..038f66e 100644
--- a/target/product/updatable_apex.mk
+++ b/target/product/updatable_apex.mk
@@ -18,3 +18,4 @@
 
 PRODUCT_PROPERTY_OVERRIDES := ro.apex.updatable=true
 PRODUCT_PACKAGES := com.android.apex.cts.shim.v1_prebuilt
+TARGET_FLATTEN_APEX := false
diff --git a/tools/droiddoc/Android.bp b/tools/droiddoc/Android.bp
new file mode 100644
index 0000000..0428068
--- /dev/null
+++ b/tools/droiddoc/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.
+
+droiddoc_exported_dir {
+    name: "droiddoc-templates-pdk",
+    path: "templates-pdk",
+}
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 99ffa31..4156c8b 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -730,6 +730,7 @@
   OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
 
   has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
+  has_boot = OPTIONS.info_dict.get("no_boot") != "true"
 
   # {vendor,odm,product,product_services}.img are unlike system.img or
   # system_other.img. Because it could be built from source, or dropped into
@@ -777,17 +778,19 @@
   def banner(s):
     logger.info("\n\n++++ " + s + " ++++\n\n")
 
-  banner("boot")
-  # common.GetBootableImage() returns the image directly if present.
-  boot_image = common.GetBootableImage(
-      "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
-  # boot.img may be unavailable in some targets (e.g. aosp_arm64).
-  if boot_image:
-    partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
-    if not os.path.exists(partitions['boot']):
-      boot_image.WriteToDir(OPTIONS.input_tmp)
-      if output_zip:
-        boot_image.AddToZip(output_zip)
+  boot_image = None
+  if has_boot:
+    banner("boot")
+    # common.GetBootableImage() returns the image directly if present.
+    boot_image = common.GetBootableImage(
+        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
+    # boot.img may be unavailable in some targets (e.g. aosp_arm64).
+    if boot_image:
+      partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
+      if not os.path.exists(partitions['boot']):
+        boot_image.WriteToDir(OPTIONS.input_tmp)
+        if output_zip:
+          boot_image.AddToZip(output_zip)
 
   recovery_image = None
   if has_recovery:
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index d14c94f..fb4ca76 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -19,11 +19,14 @@
 import re
 import shlex
 import sys
+import zipfile
 
 import common
 
 logger = logging.getLogger(__name__)
 
+OPTIONS = common.OPTIONS
+
 
 class ApexInfoError(Exception):
   """An Exception raised during Apex Information command."""
@@ -145,3 +148,72 @@
           'Failed to find {} prop in {}'.format(key, payload_path))
 
   return payload_info
+
+
+def SignApex(apex_data, payload_key, container_key, container_pw,
+             codename_to_api_level_map, signing_args=None):
+  """Signs the current APEX with the given payload/container keys.
+
+  Args:
+    apex_data: Raw APEX data.
+    payload_key: The path to payload signing key (w/ extension).
+    container_key: The path to container signing key (w/o extension).
+    container_pw: The matching password of the container_key, or None.
+    codename_to_api_level_map: A dict that maps from codename to API level.
+    signing_args: Additional args to be passed to the payload signer.
+
+  Returns:
+    The path to the signed APEX file.
+  """
+  apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
+  with open(apex_file, 'wb') as apex_fp:
+    apex_fp.write(apex_data)
+
+  APEX_PAYLOAD_IMAGE = 'apex_payload.img'
+  APEX_PUBKEY = 'apex_pubkey'
+
+  # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
+  # payload_key.
+  payload_dir = common.MakeTempDir(prefix='apex-payload-')
+  with zipfile.ZipFile(apex_file) as apex_fd:
+    payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
+
+  payload_info = ParseApexPayloadInfo(payload_file)
+  SignApexPayload(
+      payload_file,
+      payload_key,
+      payload_info['apex.key'],
+      payload_info['Algorithm'],
+      payload_info['Salt'],
+      signing_args)
+
+  # 1b. Update the embedded payload public key.
+  payload_public_key = common.ExtractAvbPublicKey(payload_key)
+
+  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
+  common.ZipDelete(apex_file, APEX_PUBKEY)
+  apex_zip = zipfile.ZipFile(apex_file, 'a')
+  common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
+  common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
+  common.ZipClose(apex_zip)
+
+  # 2. Align the files at page boundary (same as in apexer).
+  aligned_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
+  common.RunAndCheckOutput(['zipalign', '-f', '4096', apex_file, aligned_apex])
+
+  # 3. Sign the APEX container with container_key.
+  signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
+
+  # Specify the 4K alignment when calling SignApk.
+  extra_signapk_args = OPTIONS.extra_signapk_args[:]
+  extra_signapk_args.extend(['-a', '4096'])
+
+  common.SignFile(
+      aligned_apex,
+      signed_apex,
+      container_key,
+      container_pw,
+      codename_to_api_level_map=codename_to_api_level_map,
+      extra_signapk_args=extra_signapk_args)
+
+  return signed_apex
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 495fec3..e642297 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -332,10 +332,8 @@
     raise ValueError("Failed to find 'fstab_version'")
 
   if repacking:
-    # We carry a copy of file_contexts.bin under META/. If not available, search
-    # BOOT/RAMDISK/. Note that sometimes we may need a different file to build
-    # images than the one running on device, in that case, we must have the one
-    # for image generation copied to META/.
+    # "selinux_fc" should point to the file_contexts file (file_contexts.bin)
+    # under META/.
     fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
     fc_config = os.path.join(input_file, "META", fc_basename)
     assert os.path.exists(fc_config)
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index 6165d96..8fb9871 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -180,8 +180,10 @@
     OPTIONS.input_tmp = target_files
   elif zipfile.is_zipfile(target_files):
     logger.info("Building image zip from target files zip.")
-    OPTIONS.input_tmp = common.UnzipTemp(target_files,
-                                         ["IMAGES/*", "OTA/*", "META/*"])
+    # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
+    # However, common.LoadInfoDict() may read additional files under BOOT/,
+    # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
+    OPTIONS.input_tmp = common.UnzipTemp(target_files)
   else:
     raise ValueError("%s is not a valid path." % target_files)
 
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 689e095..f03cc1e 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -160,6 +160,7 @@
 
 default_other_item_list = [
     'META/boot_filesystem_config.txt',
+    'META/file_contexts.bin',
     'META/otakeys.txt',
     'META/releasetools.py',
     'META/vendor_filesystem_config.txt',
@@ -371,6 +372,63 @@
             'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
 
 
+def merge_dynamic_partition_info_dicts(system_dict,
+                                       other_dict,
+                                       include_dynamic_partition_list=True,
+                                       size_prefix='',
+                                       size_suffix='',
+                                       list_prefix='',
+                                       list_suffix=''):
+  """Merges dynamic partition info variables.
+
+  Args:
+    system_dict: The dictionary of dynamic partition info variables from the
+      partial system target files.
+    other_dict: The dictionary of dynamic partition info variables from the
+      partial other target files.
+    include_dynamic_partition_list: If true, merges the dynamic_partition_list
+      variable. Not all use cases need this variable merged.
+    size_prefix: The prefix in partition group size variables that precedes the
+      name of the partition group. For example, partition group 'group_a' with
+      corresponding size variable 'super_group_a_group_size' would have the
+      size_prefix 'super_'.
+    size_suffix: Similar to size_prefix but for the variable's suffix. For
+      example, 'super_group_a_group_size' would have size_suffix '_group_size'.
+    list_prefix: Similar to size_prefix but for the partition group's
+      partition_list variable.
+    list_suffix: Similar to size_suffix but for the partition group's
+      partition_list variable.
+
+  Returns:
+    The merged dynamic partition info dictionary.
+  """
+  merged_dict = {}
+  # Partition groups and group sizes are defined by the other (non-system)
+  # dict because these values may vary for each board that uses a shared system
+  # image.
+  merged_dict['super_partition_groups'] = other_dict['super_partition_groups']
+  if include_dynamic_partition_list:
+    system_dynamic_partition_list = system_dict.get('dynamic_partition_list',
+                                                    '')
+    other_dynamic_partition_list = other_dict.get('dynamic_partition_list', '')
+    merged_dict['dynamic_partition_list'] = (
+        '%s %s' %
+        (system_dynamic_partition_list, other_dynamic_partition_list)).strip()
+  for partition_group in merged_dict['super_partition_groups'].split(' '):
+    # Set the partition group's size using the value from the other dict.
+    key = '%s%s%s' % (size_prefix, partition_group, size_suffix)
+    if key not in other_dict:
+      raise ValueError('Other dict does not contain required key %s.' % key)
+    merged_dict[key] = other_dict[key]
+
+    # Set the partition group's partition list using a concatenation of the
+    # system and other partition lists.
+    key = '%s%s%s' % (list_prefix, partition_group, list_suffix)
+    merged_dict[key] = (
+        '%s %s' % (system_dict.get(key, ''), other_dict.get(key, ''))).strip()
+  return merged_dict
+
+
 def process_misc_info_txt(system_target_files_temp_dir,
                           other_target_files_temp_dir,
                           output_target_files_temp_dir, system_misc_info_keys):
@@ -416,107 +474,77 @@
   # Merge misc info keys used for Dynamic Partitions.
   if (merged_info_dict.get('use_dynamic_partitions') == 'true') and (
       system_info_dict.get('use_dynamic_partitions') == 'true'):
-    merged_info_dict['dynamic_partition_list'] = '%s %s' % (
-        system_info_dict.get('dynamic_partition_list', ''),
-        merged_info_dict.get('dynamic_partition_list', ''))
-    # Partition groups and group sizes are defined by the other (non-system)
-    # misc info file because these values may vary for each board that uses
-    # a shared system image.
-    for partition_group in merged_info_dict['super_partition_groups'].split(
-        ' '):
-      if ('super_%s_group_size' % partition_group) not in merged_info_dict:
-        raise ValueError(
-            'Other META/misc_info.txt does not contain required key '
-            'super_%s_group_size.' % partition_group)
-      key = 'super_%s_partition_list' % partition_group
-      merged_info_dict[key] = '%s %s' % (system_info_dict.get(
-          key, ''), merged_info_dict.get(key, ''))
+    merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
+        system_dict=system_info_dict,
+        other_dict=merged_info_dict,
+        size_prefix='super_',
+        size_suffix='_group_size',
+        list_prefix='super_',
+        list_suffix='_partition_list')
+    merged_info_dict.update(merged_dynamic_partitions_dict)
 
   output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
                                       'misc_info.txt')
-
-  sorted_keys = sorted(merged_info_dict.keys())
-
   with open(output_misc_info_txt, 'w') as output:
+    sorted_keys = sorted(merged_info_dict.keys())
     for key in sorted_keys:
       output.write('{}={}\n'.format(key, merged_info_dict[key]))
 
 
-def process_file_contexts_bin(temp_dir, output_target_files_temp_dir):
-  """Perform special processing for META/file_contexts.bin.
+def process_dynamic_partitions_info_txt(system_target_files_dir,
+                                        other_target_files_dir,
+                                        output_target_files_dir):
+  """Perform special processing for META/dynamic_partitions_info.txt.
 
-  This function combines plat_file_contexts and vendor_file_contexts, which are
-  expected to already be extracted in temp_dir, to produce a merged
-  file_contexts.bin that will land in temp_dir at META/file_contexts.bin.
+  This function merges the contents of the META/dynamic_partitions_info.txt
+  files from the system directory and the other directory, placing the merged
+  result in the output directory.
+
+  This function does nothing if META/dynamic_partitions_info.txt from the other
+  directory does not exist.
 
   Args:
-    temp_dir: The name of a scratch directory that this function can use for
-      intermediate files generated during processing.
-    output_target_files_temp_dir: The name of the working directory that must
-      already contain plat_file_contexts and vendor_file_contexts (in the
-      appropriate sub directories), and to which META/file_contexts.bin will be
-      written.
+    system_target_files_dir: The name of a directory containing the special
+      items extracted from the system target files package.
+    other_target_files_dir: The name of a directory containing the special items
+      extracted from the other target files package.
+    output_target_files_dir: The name of a directory that will be used to create
+      the output target files package after all the special cases are processed.
   """
 
-  # To create a merged file_contexts.bin file, we use the system and vendor
-  # file contexts files as input, the m4 tool to combine them, the sorting tool
-  # to sort, and finally the sefcontext_compile tool to generate the final
-  # output. We currently omit a checkfc step since the files had been checked
-  # as part of the build.
+  if not os.path.exists(
+      os.path.join(other_target_files_dir, 'META',
+                   'dynamic_partitions_info.txt')):
+    return
 
-  # The m4 step concatenates the two input files contexts files. Since m4
-  # writes to stdout, we receive that into an array of bytes, and then write it
-  # to a file.
+  def read_helper(d):
+    dynamic_partitions_info_txt = os.path.join(d, 'META',
+                                               'dynamic_partitions_info.txt')
+    with open(dynamic_partitions_info_txt) as f:
+      return list(f.read().splitlines())
 
-  # Collect the file contexts that we're going to combine from SYSTEM, VENDOR,
-  # PRODUCT, and ODM. We require SYSTEM and VENDOR, but others are optional.
+  system_dynamic_partitions_dict = common.LoadDictionaryFromLines(
+      read_helper(system_target_files_dir))
+  other_dynamic_partitions_dict = common.LoadDictionaryFromLines(
+      read_helper(other_target_files_dir))
 
-  file_contexts_list = []
+  merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
+      system_dict=system_dynamic_partitions_dict,
+      other_dict=other_dynamic_partitions_dict,
+      # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
+      include_dynamic_partition_list=False,
+      size_suffix='_size',
+      list_suffix='_partition_list')
 
-  for partition in ['SYSTEM', 'VENDOR', 'PRODUCT', 'ODM']:
-    prefix = 'plat' if partition == 'SYSTEM' else partition.lower()
-
-    file_contexts = os.path.join(output_target_files_temp_dir, partition, 'etc',
-                                 'selinux', prefix + '_file_contexts')
-
-    mandatory = partition in ['SYSTEM', 'VENDOR']
-
-    if mandatory or os.path.isfile(file_contexts):
-      file_contexts_list.append(file_contexts)
-    else:
-      logger.warning('file not found: %s', file_contexts)
-
-  command = ['m4', '--fatal-warnings', '-s'] + file_contexts_list
-
-  merged_content = common.RunAndCheckOutput(command, verbose=False)
-
-  merged_file_contexts_txt = os.path.join(temp_dir, 'merged_file_contexts.txt')
-
-  with open(merged_file_contexts_txt, 'wb') as f:
-    f.write(merged_content)
-
-  # The sort step sorts the concatenated file.
-
-  sorted_file_contexts_txt = os.path.join(temp_dir, 'sorted_file_contexts.txt')
-  command = ['fc_sort', merged_file_contexts_txt, sorted_file_contexts_txt]
-  common.RunAndWait(command, verbose=True)
-
-  # Finally, the compile step creates the final META/file_contexts.bin.
-
-  file_contexts_bin = os.path.join(output_target_files_temp_dir, 'META',
-                                   'file_contexts.bin')
-
-  command = [
-      'sefcontext_compile',
-      '-o',
-      file_contexts_bin,
-      sorted_file_contexts_txt,
-  ]
-
-  common.RunAndWait(command, verbose=True)
+  output_dynamic_partitions_info_txt = os.path.join(
+      output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
+  with open(output_dynamic_partitions_info_txt, 'w') as output:
+    sorted_keys = sorted(merged_dynamic_partitions_dict.keys())
+    for key in sorted_keys:
+      output.write('{}={}\n'.format(key, merged_dynamic_partitions_dict[key]))
 
 
-def process_special_cases(temp_dir, system_target_files_temp_dir,
+def process_special_cases(system_target_files_temp_dir,
                           other_target_files_temp_dir,
                           output_target_files_temp_dir, system_misc_info_keys,
                           rebuild_recovery):
@@ -526,8 +554,6 @@
   processing. This function performs all that special-case processing.
 
   Args:
-    temp_dir: The name of a scratch directory that this function can use for
-      intermediate files generated during processing.
     system_target_files_temp_dir: The name of a directory containing the special
       items extracted from the system target files package.
     other_target_files_temp_dir: The name of a directory containing the special
@@ -557,9 +583,10 @@
       output_target_files_temp_dir=output_target_files_temp_dir,
       system_misc_info_keys=system_misc_info_keys)
 
-  process_file_contexts_bin(
-      temp_dir=temp_dir,
-      output_target_files_temp_dir=output_target_files_temp_dir)
+  process_dynamic_partitions_info_txt(
+      system_target_files_dir=system_target_files_temp_dir,
+      other_target_files_dir=other_target_files_temp_dir,
+      output_target_files_dir=output_target_files_temp_dir)
 
 
 def merge_target_files(temp_dir, system_target_files, system_item_list,
@@ -655,7 +682,6 @@
   # files package are in place.
 
   process_special_cases(
-      temp_dir=temp_dir,
       system_target_files_temp_dir=system_target_files_temp_dir,
       other_target_files_temp_dir=other_target_files_temp_dir,
       output_target_files_temp_dir=output_target_files_temp_dir,
@@ -728,7 +754,8 @@
       output_target_files_meta_dir,
   ]
   find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
-  meta_content = common.RunAndCheckOutput(['sort'], stdin=find_process.stdout,
+  meta_content = common.RunAndCheckOutput(['sort'],
+                                          stdin=find_process.stdout,
                                           verbose=False)
 
   find_command = [
@@ -736,7 +763,8 @@
       output_target_files_meta_dir, '-prune', '-o', '-print'
   ]
   find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
-  other_content = common.RunAndCheckOutput(['sort'], stdin=find_process.stdout,
+  other_content = common.RunAndCheckOutput(['sort'],
+                                           stdin=find_process.stdout,
                                            verbose=False)
 
   with open(output_target_files_list, 'wb') as f:
@@ -766,7 +794,6 @@
     ota_from_target_files.main(ota_from_target_files_args)
 
 
-
 def call_func_with_temp_dir(func, keep_tmp):
   """Manage the creation and cleanup of the temporary directory.
 
diff --git a/tools/releasetools/sign_apex.py b/tools/releasetools/sign_apex.py
new file mode 100755
index 0000000..1778615
--- /dev/null
+++ b/tools/releasetools/sign_apex.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+
+"""
+Signs a standalone APEX file.
+
+Usage:  sign_apex [flags] input_apex_file output_apex_file
+
+  --container_key <key>
+      Mandatory flag that specifies the container signing key.
+
+  --payload_key <key>
+      Mandatory flag that specifies the payload signing key.
+
+  --payload_extra_args <args>
+      Optional flag that specifies any extra args to be passed to payload signer
+      (e.g. --payload_extra_args="--signing_helper_with_files /path/to/helper").
+"""
+
+import logging
+import shutil
+import sys
+
+import apex_utils
+import common
+
+logger = logging.getLogger(__name__)
+
+
+def main(argv):
+
+  options = {}
+
+  def option_handler(o, a):
+    if o == '--container_key':
+      # Strip the suffix if any, as common.SignFile expects no suffix.
+      DEFAULT_CONTAINER_KEY_SUFFIX = '.x509.pem'
+      if a.endswith(DEFAULT_CONTAINER_KEY_SUFFIX):
+        a = a[:-len(DEFAULT_CONTAINER_KEY_SUFFIX)]
+      options['container_key'] = a
+    elif o == '--payload_key':
+      options['payload_key'] = a
+    elif o == '--payload_extra_args':
+      options['payload_extra_args'] = a
+    else:
+      return False
+    return True
+
+  args = common.ParseOptions(
+      argv, __doc__,
+      extra_opts='',
+      extra_long_opts=[
+          'container_key=',
+          'payload_extra_args=',
+          'payload_key=',
+      ],
+      extra_option_handler=option_handler)
+
+  if (len(args) != 2 or 'container_key' not in options or
+      'payload_key' not in options):
+    common.Usage(__doc__)
+    sys.exit(1)
+
+  common.InitLogging()
+
+  input_zip = args[0]
+  output_zip = args[1]
+  with open(input_zip) as input_fp:
+    apex_data = input_fp.read()
+
+  signed_apex = apex_utils.SignApex(
+      apex_data,
+      payload_key=options['payload_key'],
+      container_key=options['container_key'],
+      container_pw=None,
+      codename_to_api_level_map=None,
+      signing_args=options.get('payload_extra_args'))
+
+  shutil.copyfile(signed_apex, output_zip)
+  logger.info("done.")
+
+
+if __name__ == '__main__':
+  try:
+    main(sys.argv[1:])
+  except common.ExternalError:
+    logger.exception("\n   ERROR:\n")
+    sys.exit(1)
+  finally:
+    common.Cleanup()
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index d25ae0c..7de0978 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -91,12 +91,15 @@
       Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
       with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
 
-  --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_algorithm <algorithm>
-  --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_key <key>
+  --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_algorithm <algorithm>
+  --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_key <key>
       Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
       the specified image. Otherwise it uses the existing values in info dict.
 
-  --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta}_extra_args <args>
+  --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_extra_args <args>
       Specify any additional args that are needed to AVB-sign the image
       (e.g. "--signing_helper /path/to/helper"). The args will be appended to
       the existing ones in info dict.
@@ -400,77 +403,6 @@
   return data
 
 
-def SignApex(apex_data, payload_key, container_key, container_pw,
-             codename_to_api_level_map, signing_args=None):
-  """Signs the current APEX with the given payload/container keys.
-
-  Args:
-    apex_data: Raw APEX data.
-    payload_key: The path to payload signing key (w/ extension).
-    container_key: The path to container signing key (w/o extension).
-    container_pw: The matching password of the container_key, or None.
-    codename_to_api_level_map: A dict that maps from codename to API level.
-    signing_args: Additional args to be passed to the payload signer.
-
-  Returns:
-    The path to the signed APEX file.
-  """
-  apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
-  with open(apex_file, 'wb') as apex_fp:
-    apex_fp.write(apex_data)
-
-  APEX_PAYLOAD_IMAGE = 'apex_payload.img'
-  APEX_PUBKEY = 'apex_pubkey'
-
-  # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
-  # payload_key.
-  payload_dir = common.MakeTempDir(prefix='apex-payload-')
-  with zipfile.ZipFile(apex_file) as apex_fd:
-    payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
-
-  payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
-  apex_utils.SignApexPayload(
-      payload_file,
-      payload_key,
-      payload_info['apex.key'],
-      payload_info['Algorithm'],
-      payload_info['Salt'],
-      signing_args)
-
-  # 1b. Update the embedded payload public key.
-  payload_public_key = common.ExtractAvbPublicKey(payload_key)
-
-  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
-  common.ZipDelete(apex_file, APEX_PUBKEY)
-  apex_zip = zipfile.ZipFile(apex_file, 'a')
-  common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
-  common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
-  common.ZipClose(apex_zip)
-
-  # 2. Align the files at page boundary (same as in apexer).
-  aligned_apex = common.MakeTempFile(
-      prefix='apex-container-', suffix='.apex')
-  common.RunAndCheckOutput(
-      ['zipalign', '-f', '4096', apex_file, aligned_apex])
-
-  # 3. Sign the APEX container with container_key.
-  signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
-
-  # Specify the 4K alignment when calling SignApk.
-  extra_signapk_args = OPTIONS.extra_signapk_args[:]
-  extra_signapk_args.extend(['-a', '4096'])
-
-  common.SignFile(
-      aligned_apex,
-      signed_apex,
-      container_key,
-      container_pw,
-      codename_to_api_level_map=codename_to_api_level_map,
-      extra_signapk_args=extra_signapk_args)
-
-  return signed_apex
-
-
 def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                        apk_keys, apex_keys, key_passwords,
                        platform_api_level, codename_to_api_level_map,
@@ -535,7 +467,7 @@
         print("           : %-*s payload   (%s)" % (
             maxsize, name, payload_key))
 
-        signed_apex = SignApex(
+        signed_apex = apex_utils.SignApex(
             data,
             payload_key,
             container_key,
@@ -966,6 +898,8 @@
       'system_other' : 'avb_system_other_add_hashtree_footer_args',
       'vendor' : 'avb_vendor_add_hashtree_footer_args',
       'vbmeta' : 'avb_vbmeta_args',
+      'vbmeta_system' : 'avb_vbmeta_system_args',
+      'vbmeta_vendor' : 'avb_vbmeta_vendor_args',
   }
 
   def ReplaceAvbPartitionSigningKey(partition):
@@ -1193,6 +1127,18 @@
       OPTIONS.avb_algorithms['vendor'] = a
     elif o == "--avb_vendor_extra_args":
       OPTIONS.avb_extra_args['vendor'] = a
+    elif o == "--avb_vbmeta_system_key":
+      OPTIONS.avb_keys['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_system_algorithm":
+      OPTIONS.avb_algorithms['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_system_extra_args":
+      OPTIONS.avb_extra_args['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_vendor_key":
+      OPTIONS.avb_keys['vbmeta_vendor'] = a
+    elif o == "--avb_vbmeta_vendor_algorithm":
+      OPTIONS.avb_algorithms['vbmeta_vendor'] = a
+    elif o == "--avb_vbmeta_vendor_extra_args":
+      OPTIONS.avb_extra_args['vbmeta_vendor'] = a
     elif o == "--avb_apex_extra_args":
       OPTIONS.avb_extra_args['apex'] = a
     else:
@@ -1232,6 +1178,12 @@
           "avb_vendor_algorithm=",
           "avb_vendor_key=",
           "avb_vendor_extra_args=",
+          "avb_vbmeta_system_algorithm=",
+          "avb_vbmeta_system_key=",
+          "avb_vbmeta_system_extra_args=",
+          "avb_vbmeta_vendor_algorithm=",
+          "avb_vbmeta_vendor_key=",
+          "avb_vbmeta_vendor_extra_args=",
       ],
       extra_option_handler=option_handler)
 
diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py
index 7e18a34..3f15d8f 100644
--- a/tools/releasetools/test_merge_target_files.py
+++ b/tools/releasetools/test_merge_target_files.py
@@ -21,7 +21,8 @@
 from merge_target_files import (read_config_list, validate_config_lists,
                                 default_system_item_list,
                                 default_other_item_list,
-                                default_system_misc_info_keys, copy_items)
+                                default_system_misc_info_keys, copy_items,
+                                merge_dynamic_partition_info_dicts)
 
 
 class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
@@ -128,3 +129,34 @@
       self.assertFalse(
           validate_config_lists(default_system_item_list, system_misc_info_keys,
                                 default_other_item_list))
+
+  def test_merge_dynamic_partition_info_dicts_ReturnsMergedDict(self):
+    system_dict = {
+        'super_partition_groups': 'group_a',
+        'dynamic_partition_list': 'system',
+        'super_group_a_list': 'system',
+    }
+    other_dict = {
+        'super_partition_groups': 'group_a group_b',
+        'dynamic_partition_list': 'vendor product',
+        'super_group_a_list': 'vendor',
+        'super_group_a_size': '1000',
+        'super_group_b_list': 'product',
+        'super_group_b_size': '2000',
+    }
+    merged_dict = merge_dynamic_partition_info_dicts(
+        system_dict=system_dict,
+        other_dict=other_dict,
+        size_prefix='super_',
+        size_suffix='_size',
+        list_prefix='super_',
+        list_suffix='_list')
+    expected_merged_dict = {
+        'super_partition_groups': 'group_a group_b',
+        'dynamic_partition_list': 'system vendor product',
+        'super_group_a_list': 'system vendor',
+        'super_group_a_size': '1000',
+        'super_group_b_list': 'product',
+        'super_group_b_size': '2000',
+    }
+    self.assertEqual(merged_dict, expected_merged_dict)