Use findstring to check MODULE_BUILD_FROM_SOURCE am: 51fb042cc2

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/+/16148239

Change-Id: I42fdff2817b0a62c80a45c6498effdd2bd3b1d69
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 47fd53a..639c4ef 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -757,6 +757,9 @@
 # vendor-ramdisk renamed to vendor_ramdisk
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor-ramdisk)
 
+# Common R directory has been removed.
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index 2d56edb..0b55c55 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -163,7 +163,9 @@
 .PHONY: ndk-docs
 endif
 
+ifeq ($(HOST_OS),linux)
 $(call dist-for-goals,sdk,$(API_FINGERPRINT))
+endif
 
 INSTALLED_RECOVERYIMAGE_TARGET :=
 # Build recovery image if
@@ -206,7 +208,7 @@
 	@mkdir -p $(dir $@)
 	$(hide) grep -v "$(subst $(space),\|,$(strip \
 	            $(sdk_build_prop_remove)))" $< > $@.tmp
-	$(hide) for x in $(sdk_build_prop_remove); do \
+	$(hide) for x in $(strip $(sdk_build_prop_remove)); do \
 	            echo "$$x"generic >> $@.tmp; done
 	$(hide) mv $@.tmp $@
 
@@ -320,11 +322,11 @@
 define build-image-kernel-modules-depmod
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: .KATI_IMPLICIT_OUTPUTS := $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.alias $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.softdep $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: $(DEPMOD)
-$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(1)
+$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULES := $(strip $(1))
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MOUNT_POINT := $(2)
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_DIR := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(2)/lib/modules/$(8)
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_STAGING_DIR := $(3)
-$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(4)
+$(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_MODULES := $(strip $(4))
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_LOAD_FILE := $(3)/$(DEPMOD_STAGING_SUBDIR)/$(5)
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_MODULE_ARCHIVE := $(6)
 $(3)/$(DEPMOD_STAGING_SUBDIR)/modules.dep: PRIVATE_OUTPUT_DIR := $(7)
@@ -487,6 +489,12 @@
   endif
 endif
 
+ifneq ($(BOARD_DO_NOT_STRIP_RECOVERY_MODULES),true)
+	RECOVERY_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_recovery_stripped)
+else
+	RECOVERY_STRIPPED_MODULE_STAGING_DIR :=
+endif
+
 ifneq ($(BOARD_DO_NOT_STRIP_VENDOR_MODULES),true)
 	VENDOR_STRIPPED_MODULE_STAGING_DIR := $(call intermediates-dir-for,PACKAGING,depmod_vendor_stripped)
 else
@@ -501,7 +509,7 @@
 
 BOARD_KERNEL_MODULE_DIRS += top
 $(foreach kmd,$(BOARD_KERNEL_MODULE_DIRS), \
-  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,,$(kmd))) \
+  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,RECOVERY,$(TARGET_RECOVERY_ROOT_OUT),,modules.load.recovery,$(RECOVERY_STRIPPED_MODULE_STAGING_DIR),$(kmd))) \
   $(eval vendor_ramdisk_fragment := $(KERNEL_MODULE_DIR_VENDOR_RAMDISK_FRAGMENT_$(kmd))) \
   $(if $(vendor_ramdisk_fragment), \
     $(eval output_dir := $(VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk_fragment).STAGING_DIR)) \
@@ -769,7 +777,9 @@
 	$(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json)
 	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
 
-$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_ROOT))
+ifeq ($(HOST_OS),linux)
+$(call dist-for-goals, sdk sdk_addon, $(INSTALLED_FILES_FILE_ROOT))
+endif
 
 #------------------------------------------------------------------
 # dtb
@@ -799,7 +809,9 @@
 	$(FILESLIST) $(TARGET_RAMDISK_OUT) > $(@:.txt=.json)
 	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
 
+ifeq ($(HOST_OS),linux)
 $(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_RAMDISK))
+endif
 BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
 
 ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
@@ -815,8 +827,13 @@
 
 # We just build this directly to the install location.
 INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
+$(INSTALLED_RAMDISK_TARGET): PRIVATE_DIRS := debug_ramdisk dev metadata mnt proc second_stage_resources sys
 $(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) $(INSTALLED_FILES_FILE_RAMDISK) | $(COMPRESSION_COMMAND_DEPS)
 	$(call pretty,"Target ramdisk: $@")
+	$(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(PRIVATE_DIRS))
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
+	$(hide) mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(PRIVATE_DIRS))
+endif
 	$(hide) $(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@
 
 .PHONY: ramdisk-nodeps
@@ -1085,12 +1102,10 @@
 $(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
 	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
 
-ifeq (true,$(BOARD_BUILD_VENDOR_RAMDISK_IMAGE))
 INSTALLED_VENDOR_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk.img
 $(INSTALLED_VENDOR_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET)
-	$(call pretty,"Target vendor ramdisk: $@")
+	@echo "Target vendor ramdisk: $@"
 	$(copy-file-to-target)
-endif
 
 INSTALLED_FILES_FILE_VENDOR_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk.txt
 INSTALLED_FILES_JSON_VENDOR_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_RAMDISK:.txt=.json)
@@ -1590,31 +1605,54 @@
 
 endif # PRODUCT_USE_DYNAMIC_PARTITIONS
 
+# $(1) the partition name (eg system)
+# $(2) the image prop file
+define add-common-flags-to-image-props
+$(eval _var := $(call to-upper,$(1)))
+$(hide) echo "$(1)_selinux_fc=$(SELINUX_FC)" >> $(2)
+$(hide) echo "building_$(1)_image=$(BUILDING_$(_var)_IMAGE)" >> $(2)
+endef
+
+# $(1) the partition name (eg system)
+# $(2) the image prop file
+define add-common-ro-flags-to-image-props
+$(eval _var := $(call to-upper,$(1)))
+$(if $(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR),$(hide) echo "$(1)_erofs_compressor=$(BOARD_$(_var)IMAGE_EROFS_COMPRESSOR)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE),$(hide) echo "$(1)_erofs_pcluster_size=$(BOARD_$(_var)IMAGE_EROFS_PCLUSTER_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo "$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo "$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo "$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo "$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "$(1)_reserved_size=$(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_PARTITION_SIZE),$(hide) echo "$(1)_size=$(BOARD_$(_var)IMAGE_PARTITION_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "$(1)_squashfs_block_size=$(BOARD_$(_var)IMAGE_SQUASHFS_BLOCK_SIZE)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "$(1)_squashfs_compressor=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "$(1)_squashfs_compressor_opt=$(BOARD_$(_var)IMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "$(1)_squashfs_disable_4k_align=$(BOARD_$(_var)IMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(2))
+$(if $(PRODUCT_$(_var)_BASE_FS_PATH),$(hide) echo "$(1)_base_fs_file=$(PRODUCT_$(_var)_BASE_FS_PATH)" >> $(2))
+$(eval _size := $(BOARD_$(_var)IMAGE_PARTITION_SIZE))
+$(eval _reserved := $(BOARD_$(_var)IMAGE_PARTITION_RESERVED_SIZE))
+$(eval _headroom := $(PRODUCT_$(_var)_HEADROOM))
+$(if $(or $(_size), $(_reserved), $(_headroom)),,
+    $(hide) echo "$(1)_disable_sparse=true" >> $(2))
+$(call add-common-flags-to-image-props,$(1),$(2))
+endef
+
 # $(1): the path of the output dictionary file
 # $(2): a subset of "system vendor cache userdata product system_ext oem odm vendor_dlkm odm_dlkm"
 # $(3): additional "key=value" pairs to append to the dictionary file.
 define generate-image-prop-dictionary
 $(if $(filter $(2),system),\
-    $(if $(BOARD_SYSTEMIMAGE_PARTITION_SIZE),$(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(1))
     $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_fs_type=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "system_f2fs_compress=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "system_f2fs_sldc_flags=$(BOARD_SYSTEMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "system_extfs_inode_count=$(BOARD_SYSTEMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "system_extfs_rsv_pct=$(BOARD_SYSTEMIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_JOURNAL_SIZE),$(hide) echo "system_journal_size=$(BOARD_SYSTEMIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_squashfs_compressor=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_squashfs_compressor_opt=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "system_squashfs_block_size=$(BOARD_SYSTEMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "system_squashfs_disable_4k_align=$(BOARD_SYSTEMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(PRODUCT_SYSTEM_BASE_FS_PATH),$(hide) echo "system_base_fs_file=$(PRODUCT_SYSTEM_BASE_FS_PATH)" >> $(1))
     $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1))
-    $(if $(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "system_reserved_size=$(BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "system_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_system_image=$(BUILDING_SYSTEM_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,system,$(1))
 )
 $(if $(filter $(2),system_other),\
     $(hide) echo "building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)" >> $(1)
+    $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),,
+        $(hide) echo "system_other_disable_sparse=true" >> $(1))
 )
 $(if $(filter $(2),userdata),\
     $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1622,116 +1660,37 @@
     $(if $(PRODUCT_FS_CASEFOLD),$(hide) echo "needs_casefold=$(PRODUCT_FS_CASEFOLD)" >> $(1))
     $(if $(PRODUCT_QUOTA_PROJID),$(hide) echo "needs_projid=$(PRODUCT_QUOTA_PROJID)" >> $(1))
     $(if $(PRODUCT_FS_COMPRESSION),$(hide) echo "needs_compress=$(PRODUCT_FS_COMPRESSION)" >> $(1))
-    $(hide) echo "userdata_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_userdata_image=$(BUILDING_USERDATA_IMAGE)" >> $(1)
+    $(call add-common-flags-to-image-props,userdata,$(1))
 )
 $(if $(filter $(2),cache),\
     $(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
     $(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
-    $(hide) echo "cache_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_cache_image=$(BUILDING_CACHE_IMAGE)" >> $(1)
+    $(call add-common-flags-to-image-props,cache,$(1))
 )
 $(if $(filter $(2),vendor),\
-    $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "vendor_f2fs_compress=$(BOARD_VENDORIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "vendor_f2fs_sldc_flags=$(BOARD_VENDORIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_EXTFS_INODE_COUNT),$(hide) echo "vendor_extfs_inode_count=$(BOARD_VENDORIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_EXTFS_RSV_PCT),$(hide) echo "vendor_extfs_rsv_pct=$(BOARD_VENDORIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_PARTITION_SIZE),$(hide) echo "vendor_size=$(BOARD_VENDORIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_journal_size=$(BOARD_VENDORIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "vendor_squashfs_compressor=$(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "vendor_squashfs_compressor_opt=$(BOARD_VENDORIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "vendor_squashfs_block_size=$(BOARD_VENDORIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "vendor_squashfs_disable_4k_align=$(BOARD_VENDORIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(PRODUCT_VENDOR_BASE_FS_PATH),$(hide) echo "vendor_base_fs_file=$(PRODUCT_VENDOR_BASE_FS_PATH)" >> $(1))
-    $(if $(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "vendor_reserved_size=$(BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "vendor_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_vendor_image=$(BUILDING_VENDOR_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,vendor,$(1))
 )
 $(if $(filter $(2),product),\
-    $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "product_fs_type=$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "product_f2fs_compress=$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "product_f2fs_sldc_flags=$(BOARD_PRODUCTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_EXTFS_INODE_COUNT),$(hide) echo "product_extfs_inode_count=$(BOARD_PRODUCTIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_EXTFS_RSV_PCT),$(hide) echo "product_extfs_rsv_pct=$(BOARD_PRODUCTIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_PARTITION_SIZE),$(hide) echo "product_size=$(BOARD_PRODUCTIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_JOURNAL_SIZE),$(hide) echo "product_journal_size=$(BOARD_PRODUCTIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "product_squashfs_compressor=$(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "product_squashfs_compressor_opt=$(BOARD_PRODUCTIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "product_squashfs_block_size=$(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "product_squashfs_disable_4k_align=$(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(PRODUCT_PRODUCT_BASE_FS_PATH),$(hide) echo "product_base_fs_file=$(PRODUCT_PRODUCT_BASE_FS_PATH)" >> $(1))
-    $(if $(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "product_reserved_size=$(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "product_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_product_image=$(BUILDING_PRODUCT_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,product,$(1))
 )
 $(if $(filter $(2),system_ext),\
-    $(if $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_ext_fs_type=$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "system_ext_f2fs_compress=$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "system_ext_f2fs_sldc_flags=$(BOARD_SYSTEM_EXTIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_EXTFS_INODE_COUNT),$(hide) echo "system_ext_extfs_inode_count=$(BOARD_SYSTEM_EXTIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_EXTFS_RSV_PCT),$(hide) echo "system_ext_extfs_rsv_pct=$(BOARD_SYSTEM_EXTIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE),$(hide) echo "system_ext_size=$(BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_JOURNAL_SIZE),$(hide) echo "system_ext_journal_size=$(BOARD_SYSTEM_EXTIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_ext_squashfs_compressor=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_ext_squashfs_compressor_opt=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "system_ext_squashfs_block_size=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "system_ext_squashfs_disable_4k_align=$(BOARD_SYSTEM_EXTIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "system_ext_reserved_size=$(BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "system_ext_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_system_ext_image=$(BUILDING_SYSTEM_EXT_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,system_ext,$(1))
 )
 $(if $(filter $(2),odm),\
-    $(if $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "odm_fs_type=$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "odm_extfs_inode_count=$(BOARD_ODMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_EXTFS_RSV_PCT),$(hide) echo "odm_extfs_rsv_pct=$(BOARD_ODMIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_PARTITION_SIZE),$(hide) echo "odm_size=$(BOARD_ODMIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_JOURNAL_SIZE),$(hide) echo "odm_journal_size=$(BOARD_ODMIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "odm_squashfs_compressor=$(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "odm_squashfs_compressor_opt=$(BOARD_ODMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "odm_squashfs_block_size=$(BOARD_ODMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "odm_squashfs_disable_4k_align=$(BOARD_ODMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(PRODUCT_ODM_BASE_FS_PATH),$(hide) echo "odm_base_fs_file=$(PRODUCT_ODM_BASE_FS_PATH)" >> $(1))
-    $(if $(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "odm_reserved_size=$(BOARD_ODMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "odm_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_odm_image=$(BUILDING_ODM_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,odm,$(1))
 )
 $(if $(filter $(2),vendor_dlkm),\
-    $(if $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_dlkm_fs_type=$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "vendor_dlkm_f2fs_compress=$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_COMPRESS)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "vendor_dlkm_f2fs_sldc_flags=$(BOARD_VENDOR_DLKMIMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "vendor_dlkm_extfs_inode_count=$(BOARD_VENDOR_DLKMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_EXTFS_RSV_PCT),$(hide) echo "vendor_dlkm_extfs_rsv_pct=$(BOARD_VENDOR_DLKMIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE),$(hide) echo "vendor_dlkm_size=$(BOARD_VENDOR_DLKMIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_dlkm_journal_size=$(BOARD_VENDOR_DLKMIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "vendor_dlkm_squashfs_compressor=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "vendor_dlkm_squashfs_compressor_opt=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "vendor_dlkm_squashfs_block_size=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "vendor_dlkm_squashfs_disable_4k_align=$(BOARD_VENDOR_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "vendor_dlkm_reserved_size=$(BOARD_VENDOR_DLKMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "vendor_dlkm_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_vendor_dlkm_image=$(BUILDING_VENDOR_DLKM_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,vendor_dlkm,$(1))
 )
 $(if $(filter $(2),odm_dlkm),\
-    $(if $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "odm_dlkm_fs_type=$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "odm_dlkm_extfs_inode_count=$(BOARD_ODM_DLKMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_EXTFS_RSV_PCT),$(hide) echo "odm_dlkm_extfs_rsv_pct=$(BOARD_ODM_DLKMIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE),$(hide) echo "odm_dlkm_size=$(BOARD_ODM_DLKMIMAGE_PARTITION_SIZE)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_JOURNAL_SIZE),$(hide) echo "odm_dlkm_journal_size=$(BOARD_ODM_DLKMIMAGE_JOURNAL_SIZE)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "odm_dlkm_squashfs_compressor=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "odm_dlkm_squashfs_compressor_opt=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "odm_dlkm_squashfs_block_size=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "odm_dlkm_squashfs_disable_4k_align=$(BOARD_ODM_DLKMIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
-    $(if $(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "odm_dlkm_reserved_size=$(BOARD_ODM_DLKMIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
-    $(hide) echo "odm_dlkm_selinux_fc=$(SELINUX_FC)" >> $(1)
-    $(hide) echo "building_odm_dlkm_image=$(BUILDING_ODM_DLKM_IMAGE)" >> $(1)
+    $(call add-common-ro-flags-to-image-props,odm_dlkm,$(1))
 )
 $(if $(filter $(2),oem),\
     $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
     $(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1))
     $(if $(BOARD_OEMIMAGE_EXTFS_INODE_COUNT),$(hide) echo "oem_extfs_inode_count=$(BOARD_OEMIMAGE_EXTFS_INODE_COUNT)" >> $(1))
     $(if $(BOARD_OEMIMAGE_EXTFS_RSV_PCT),$(hide) echo "oem_extfs_rsv_pct=$(BOARD_OEMIMAGE_EXTFS_RSV_PCT)" >> $(1))
-    $(hide) echo "oem_selinux_fc=$(SELINUX_FC)" >> $(1)
+    $(call add-common-flags-to-image-props,oem,$(1))
 )
 $(hide) echo "ext_mkuserimg=$(notdir $(MKEXTUSERIMG))" >> $(1)
 
@@ -1740,6 +1699,9 @@
 $(if $(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG),$(hide) echo "erofs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG),$(hide) echo "squashfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG),$(hide) echo "f2fs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG)" >> $(1))
+$(if $(BOARD_EROFS_COMPRESSOR),$(hide) echo "erofs_default_compressor=$(BOARD_EROFS_COMPRESSOR)" >> $(1))
+$(if $(BOARD_EROFS_PCLUSTER_SIZE),$(hide) echo "erofs_pcluster_size=$(BOARD_EROFS_PCLUSTER_SIZE)" >> $(1))
+$(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo "erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)" >> $(1))
 $(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1))
 $(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo "flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)" >> $(1))
 $(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo "flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)" >> $(1))
@@ -2327,79 +2289,75 @@
 $(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.)
 endif
 
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-# -----------------------------------------------------------------
-# the debug ramdisk, which is the original ramdisk plus additional
-# files: force_debuggable, adb_debug.prop and userdebug sepolicy.
-# When /force_debuggable is present, /init will load userdebug sepolicy
-# and property files to allow adb root, if the device is unlocked.
 
-ifdef BUILDING_RAMDISK_IMAGE
-BUILT_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img
-INSTALLED_DEBUG_RAMDISK_TARGET := $(BUILT_DEBUG_RAMDISK_TARGET)
+# -----------------------------------------------------------------
+# Build debug ramdisk and debug boot image.
+ifneq ($(BUILDING_DEBUG_BOOT_IMAGE)$(BUILDING_DEBUG_VENDOR_BOOT_IMAGE),)
 
 INTERNAL_DEBUG_RAMDISK_FILES := $(filter $(TARGET_DEBUG_RAMDISK_OUT)/%, \
     $(ALL_GENERATED_SOURCES) \
     $(ALL_DEFAULT_INSTALLED_MODULES))
 
-INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt
-INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json)
-$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK)
-
+# Directories to be picked into the debug ramdisk.
+# As these directories are all merged into one single cpio archive, the order
+# matters. If there are multiple files with the same pathname, then the last one
+# wins.
+#
 # ramdisk-debug.img will merge the content from either ramdisk.img or
 # ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT
 # is set or not.
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-  $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(recovery_ramdisk)
-else
-  $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-  $(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
+  INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RECOVERY_ROOT_OUT)
+  INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(recovery_ramdisk)
+else  # BOARD_USES_RECOVERY_AS_BOOT == true
+  INTERNAL_DEBUG_RAMDISK_SRC_DIRS := $(TARGET_RAMDISK_OUT)
+  INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET := $(INSTALLED_RAMDISK_TARGET)
+endif # BOARD_USES_RECOVERY_AS_BOOT != true
 
-$(INSTALLED_FILES_FILE_DEBUG_RAMDISK) : $(INTERNAL_DEBUG_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)
-	@echo Installed file list: $@
-	mkdir -p $(dir $@)
-	rm -f $@
-	$(FILESLIST) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) > $(@:.txt=.json)
+INTERNAL_DEBUG_RAMDISK_SRC_DIRS += $(TARGET_DEBUG_RAMDISK_OUT)
+INTERNAL_DEBUG_RAMDISK_SRC_DEPS := $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET) $(INTERNAL_DEBUG_RAMDISK_FILES)
+
+# INSTALLED_FILES_FILE_DEBUG_RAMDISK would ensure TARGET_DEBUG_RAMDISK_OUT is created.
+INSTALLED_FILES_FILE_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-ramdisk-debug.txt
+INSTALLED_FILES_JSON_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK:.txt=.json)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_DEBUG_RAMDISK)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(INTERNAL_DEBUG_RAMDISK_SRC_DEPS)
+$(INSTALLED_FILES_FILE_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)
+	@echo "Installed file list: $@"
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@) $(TARGET_DEBUG_RAMDISK_OUT)
+	touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
+	$(FILESLIST) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) > $(@:.txt=.json)
 	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
 
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  $(INSTALLED_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-  $(INSTALLED_DEBUG_RAMDISK_TARGET): $(recovery_ramdisk)
-else
-  $(INSTALLED_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-  $(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
+ifdef BUILDING_DEBUG_BOOT_IMAGE
+
+# -----------------------------------------------------------------
+# the debug ramdisk, which is the original ramdisk plus additional
+# files: force_debuggable, adb_debug.prop and userdebug sepolicy.
+# When /force_debuggable is present, /init will load userdebug sepolicy
+# and property files to allow adb root, if the device is unlocked.
+INSTALLED_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-debug.img
 
 $(INSTALLED_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)
-$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_DEBUG_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
-	$(call pretty,"Target debug ramdisk: $@")
-	mkdir -p $(TARGET_DEBUG_RAMDISK_OUT)
-	touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INSTALLED_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+	@echo "Target debug ramdisk: $@"
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
 
 .PHONY: ramdisk_debug-nodeps
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  ramdisk_debug-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-else
-  ramdisk_debug-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-endif # BOARD_USES_RECOVERY_AS_BOOT
 ramdisk_debug-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
-	echo "make $@: ignoring dependencies"
-	mkdir -p $(TARGET_DEBUG_RAMDISK_OUT)
-	touch $(TARGET_DEBUG_RAMDISK_OUT)/force_debuggable
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET)
-
-endif # BUILDING_RAMDISK_IMAGE
+	@echo "make $@: ignoring dependencies"
+	$(hide) rm -f $(INSTALLED_DEBUG_RAMDISK_TARGET)
+	$(hide) mkdir -p $(dir $(INSTALLED_DEBUG_RAMDISK_TARGET)) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_DEBUG_RAMDISK_TARGET)
 
 # -----------------------------------------------------------------
 # the boot-debug.img, which is the kernel plus ramdisk-debug.img
 #
 # Note: it's intentional to skip signing for boot-debug.img, because it
 # can only be used if the device is unlocked with verification error.
-ifneq ($(INSTALLED_BOOTIMAGE_TARGET),)
-ifneq ($(strip $(TARGET_NO_KERNEL)),true)
 ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)
   INSTALLED_DEBUG_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-debug,$(BOARD_KERNEL_BINARIES)), \
          $(PRODUCT_OUT)/$(k).img)
@@ -2408,10 +2366,11 @@
 endif
 
 # Replace ramdisk.img in $(MKBOOTIMG) ARGS with ramdisk-debug.img to build boot-debug.img
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(recovery_ramdisk),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_RECOVERYIMAGE_ARGS))
+  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET),$(INTERNAL_RECOVERYIMAGE_ARGS))
 else
-  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INSTALLED_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_BOOTIMAGE_ARGS))
+  INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INTERNAL_DEBUG_RAMDISK_SRC_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
@@ -2445,7 +2404,7 @@
 endef
 
 # 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) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
 	$(call pretty,"Target boot debug image: $@")
 	$(call build-debug-bootimage-target, $@)
 
@@ -2454,55 +2413,57 @@
 	echo "make $@: ignoring dependencies"
 	$(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b))
 
-endif # TARGET_NO_KERNEL
-endif # INSTALLED_BOOTIMAGE_TARGET
+endif # BUILDING_DEBUG_BOOT_IMAGE
 
-ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
-ifeq ($(BUILDING_RAMDISK_IMAGE),true)
 # -----------------------------------------------------------------
 # vendor debug ramdisk
 # Combines vendor ramdisk files and debug ramdisk files to build the vendor debug ramdisk.
-#
+ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
 INTERNAL_VENDOR_DEBUG_RAMDISK_FILES := $(filter $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)/%, \
     $(ALL_GENERATED_SOURCES) \
     $(ALL_DEFAULT_INSTALLED_MODULES))
 
-INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt
-INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
-$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(FILESLIST) $(FILESLIST_UTIL)
-	@echo Installed file list: $@
-	mkdir -p $(dir $@)
-	rm -f $@
-	mkdir -p $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) # The dir might not be created if no modules are installed here.
-	$(FILESLIST) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) > $(@:.txt=.json)
-	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
-
-INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT)
+# The debug vendor ramdisk combines vendor ramdisk and debug ramdisk.
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS := $(TARGET_VENDOR_RAMDISK_OUT)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS := $(INTERNAL_VENDOR_RAMDISK_TARGET)
 
 # Exclude recovery files in the default vendor ramdisk if including a standalone
 # recovery ramdisk in vendor_boot.
 ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))
-ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-endif
-endif
+  ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
+    INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_RECOVERY_ROOT_OUT)
+    INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
+  endif # BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT != true
+endif # BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT == true
 
-# The vendor debug ramdisk combines vendor ramdisk and debug ramdisk.
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS += $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT)
+INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS += $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) $(INSTALLED_FILES_FILE_DEBUG_RAMDISK)
+
+# INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK would ensure TARGET_VENDOR_DEBUG_RAMDISK_OUT is created.
+INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK := $(PRODUCT_OUT)/installed-files-vendor-ramdisk-debug.txt
+INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK:.txt=.json)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR_DEBUG_RAMDISK)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DEPS)
+$(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK): $(FILESLIST) $(FILESLIST_UTIL)
+	@echo "Installed file list: $@"
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)
+	$(FILESLIST) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) > $(@:.txt=.json)
+	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
+
+INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-debug)/vendor_ramdisk-debug.cpio$(RAMDISK_EXT)
+
 $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK)
-$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_DEBUG_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
-	mkdir -p $(TARGET_VENDOR_DEBUG_RAMDISK_OUT)
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_DEBUG_RAMDISK_OUT) $(TARGET_VENDOR_DEBUG_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
 
-ifeq (true,$(BOARD_BUILD_VENDOR_RAMDISK_IMAGE))
 INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-debug.img
 $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET): $(INTERNAL_VENDOR_DEBUG_RAMDISK_TARGET)
-	$(call pretty,"Target vendor debug ramdisk: $@")
+	@echo "Target debug vendor ramdisk: $@"
 	$(copy-file-to-target)
-endif
 
 # -----------------------------------------------------------------
 # vendor_boot-debug.img.
@@ -2534,76 +2495,64 @@
 	$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))
 	$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))
 
-endif # BUILDING_RAMDISK_IMAGE
-endif # BUILDING_VENDOR_BOOT_IMAGE
+endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+# Appends a few test harness specific properties into the adb_debug.prop.
+ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1
+ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1
+
+INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET := $(strip $(filter $(TARGET_DEBUG_RAMDISK_OUT)/adb_debug.prop,$(INTERNAL_DEBUG_RAMDISK_FILES)))
+INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop
+$(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET): $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET)
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+ifdef INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET
+	$(hide) cp $(INTERNAL_DEBUG_RAMDISK_ADB_DEBUG_PROP_TARGET) $@
+endif
+	$(hide) echo "" >> $@
+	$(hide) echo "#" >> $@
+	$(hide) echo "# ADDITIONAL TEST HARNESS PROPERTIES" >> $@
+	$(hide) echo "#" >> $@
+	$(hide) $(foreach line,$(ADDITIONAL_TEST_HARNESS_PROPERTIES), \
+	          echo "$(line)" >> $@;)
+
+INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \
+    $(INTERNAL_TEST_HARNESS_RAMDISK_ADB_DEBUG_PROP_TARGET) \
+    $(ALL_GENERATED_SOURCES) \
+    $(ALL_DEFAULT_INSTALLED_MODULES))
+
+# The order is important here. The test harness ramdisk staging directory has to
+# come last so that it can override the adb_debug.prop in the debug ramdisk
+# staging directory.
+INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)
+INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)
+
+ifdef BUILDING_DEBUG_BOOT_IMAGE
 
 # -----------------------------------------------------------------
 # The test harness ramdisk, which is based off debug_ramdisk, plus a
 # few additional test-harness-specific properties in adb_debug.prop.
+INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img
 
-ifdef BUILDING_RAMDISK_IMAGE
-BUILT_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img
-INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(BUILT_TEST_HARNESS_RAMDISK_TARGET)
-
-# Appends a few test harness specific properties into the adb_debug.prop.
-TEST_HARNESS_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop
-ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1
-ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1
-
-# $(1): a list of key=value pairs for additional property assignments
-# $(2): the target .prop file to append the properties from $(1)
-define append-test-harness-props
-  echo "#" >> $(2); \
-  echo "# ADDITIONAL TEST HARNESS_PROPERTIES" >> $(2); \
-  echo "#" >> $(2);
-  $(foreach line,$(1), echo "$(line)" >> $(2);)
-endef
-
-INTERNAL_TEST_HARNESS_RAMDISK_FILES := $(filter $(TARGET_TEST_HARNESS_RAMDISK_OUT)/%, \
-    $(ALL_GENERATED_SOURCES) \
-    $(ALL_DEFAULT_INSTALLED_MODULES))
-
-# ramdisk-test-harness.img will merge the content from either ramdisk.img or
-# ramdisk-recovery.img, depending on whether BOARD_USES_RECOVERY_AS_BOOT is set
-# or not.
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-  $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(recovery_ramdisk)
-else
-  $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-  $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INSTALLED_RAMDISK_TARGET)
-endif # BOARD_USES_RECOVERY_AS_BOOT
-
-# The test harness ramdisk will rsync the files from the debug ramdisk, then appends some props.
-$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)
-$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
-	$(call pretty,"Target test harness ramdisk: $@")
-	rsync --chmod=u+w -a $(TARGET_DEBUG_RAMDISK_OUT)/ $(TARGET_TEST_HARNESS_RAMDISK_OUT)
-	$(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DEPS)
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
+	@echo "Target test harness ramdisk: $@"
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
 
 .PHONY: ramdisk_test_harness-nodeps
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-  ramdisk_test_harness-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-else
-  ramdisk_test_harness-nodeps: PRIVATE_ADDITIONAL_DIR := $(TARGET_RAMDISK_OUT)
-endif # BOARD_USES_RECOVERY_AS_BOOT
 ramdisk_test_harness-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
-	echo "make $@: ignoring dependencies"
-	rsync --chmod=u+w -a $(TARGET_DEBUG_RAMDISK_OUT)/ $(TARGET_TEST_HARNESS_RAMDISK_OUT)
-	$(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
-
-endif # BUILDING_RAMDISK_IMAGE
+	@echo "make $@: ignoring dependencies"
+	$(hide) rm -f $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+	$(hide) mkdir -p $(dir $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
 
 # -----------------------------------------------------------------
 # the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img
 #
 # Note: it's intentional to skip signing for boot-test-harness.img, because it
 # can only be used if the device is unlocked with verification error.
-ifneq ($(INSTALLED_BOOTIMAGE_TARGET),)
-ifneq ($(strip $(TARGET_NO_KERNEL)),true)
-
 ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)
   INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot-test-harness,$(BOARD_KERNEL_BINARIES)), \
     $(PRODUCT_OUT)/$(k).img)
@@ -2612,6 +2561,7 @@
 endif
 
 # Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
 INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS))
 
 # If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader
@@ -2631,8 +2581,7 @@
 endef
 
 # Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img.
-$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
-$(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
 	$(call pretty,"Target boot test harness image: $@")
 	$(call build-boot-test-harness-target,$@)
 
@@ -2641,31 +2590,31 @@
 	echo "make $@: ignoring dependencies"
 	$(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b))
 
-endif # TARGET_NO_KERNEL
-endif # INSTALLED_BOOTIMAGE_TARGET
-endif # BOARD_BUILD_SYSTEM_ROOT_IMAGE is not true
+endif # BUILDING_DEBUG_BOOT_IMAGE
 
-ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
-ifeq ($(BUILDING_RAMDISK_IMAGE),true)
 # -----------------------------------------------------------------
 # vendor test harness ramdisk, which is a vendor ramdisk combined with
 # a test harness ramdisk.
+ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
 
 INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor_boot-test-harness)/vendor_ramdisk-test-harness.cpio$(RAMDISK_EXT)
 
-# Exclude recovery files in the default vendor ramdisk if including a standalone
-# recovery ramdisk in vendor_boot.
-ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))
-ifneq (true,$(BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT))
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): PRIVATE_ADDITIONAL_DIR := $(TARGET_RECOVERY_ROOT_OUT)
-endif
-endif
+# The order is important here. The test harness ramdisk staging directory has to
+# come last so that it can override the adb_debug.prop in the debug ramdisk
+# staging directory.
+INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS := $(INTERNAL_DEBUG_VENDOR_RAMDISK_SRC_DIRS) $(TARGET_TEST_HARNESS_RAMDISK_OUT)
+INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS := $(INSTALLED_FILES_FILE_VENDOR_DEBUG_RAMDISK) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES)
 
-# The vendor test harness ramdisk combines vendor ramdisk and test harness ramdisk.
-$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_RAMDISK_TARGET) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+$(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DEPS)
 $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)
-	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) $(TARGET_TEST_HARNESS_RAMDISK_OUT) $(PRIVATE_ADDITIONAL_DIR) | $(COMPRESSION_COMMAND) > $@
+	$(hide) rm -f $@
+	$(hide) mkdir -p $(dir $@)
+	$(MKBOOTFS) -d $(TARGET_OUT) $(INTERNAL_TEST_HARNESS_VENDOR_RAMDISK_SRC_DIRS) | $(COMPRESSION_COMMAND) > $@
+
+INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/vendor_ramdisk-test-harness.img
+$(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET): $(INTERNAL_VENDOR_TEST_HARNESS_RAMDISK_TARGET)
+	@echo "Target test harness vendor ramdisk: $@"
+	$(copy-file-to-target)
 
 # -----------------------------------------------------------------
 # vendor_boot-test-harness.img.
@@ -2684,8 +2633,10 @@
 	$(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE))
 	$(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@))
 
-endif # BUILDING_RAMDISK_IMAGE
-endif # BUILDING_VENDOR_BOOT_IMAGE
+endif # BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+endif # BUILDING_DEBUG_BOOT_IMAGE || BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
 
 # Creates a compatibility symlink between two partitions, e.g. /system/vendor to /vendor
 # $1: from location (e.g $(TARGET_OUT)/vendor)
@@ -2772,7 +2723,9 @@
 .PHONY: installed-file-list
 installed-file-list: $(INSTALLED_FILES_FILE)
 
-$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE))
+ifeq ($(HOST_OS),linux)
+$(call dist-for-goals, sdk sdk_addon, $(INSTALLED_FILES_FILE))
+endif
 
 systemimage_intermediates := \
     $(call intermediates-dir-for,PACKAGING,systemimage)
@@ -3278,7 +3231,8 @@
   $(call pretty,"Target odm fs image: $(INSTALLED_ODMIMAGE_TARGET)")
   @mkdir -p $(TARGET_OUT_ODM)
   @mkdir -p $(odmimage_intermediates) && rm -rf $(odmimage_intermediates)/odm_image_info.txt
-  $(call generate-userimage-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, skip_fsck=true)
+  $(call generate-image-prop-dictionary, $(odmimage_intermediates)/odm_image_info.txt, odm, \
+	  skip_fsck=true)
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(TARGET_OUT_ODM) $(odmimage_intermediates)/odm_image_info.txt \
@@ -3329,7 +3283,8 @@
   $(call pretty,"Target vendor_dlkm fs image: $(INSTALLED_VENDOR_DLKMIMAGE_TARGET)")
   @mkdir -p $(TARGET_OUT_VENDOR_DLKM)
   @mkdir -p $(vendor_dlkmimage_intermediates) && rm -rf $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt
-  $(call generate-userimage-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, skip_fsck=true)
+  $(call generate-image-prop-dictionary, $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt, \
+	  vendor_dlkm, skip_fsck=true)
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(TARGET_OUT_VENDOR_DLKM) $(vendor_dlkmimage_intermediates)/vendor_dlkm_image_info.txt \
@@ -3380,7 +3335,8 @@
   $(call pretty,"Target odm_dlkm fs image: $(INSTALLED_ODM_DLKMIMAGE_TARGET)")
   @mkdir -p $(TARGET_OUT_ODM_DLKM)
   @mkdir -p $(odm_dlkmimage_intermediates) && rm -rf $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt
-  $(call generate-userimage-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, skip_fsck=true)
+  $(call generate-image-prop-dictionary, $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt, \
+	  odm_dlkm, skip_fsck=true)
   PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
       $(BUILD_IMAGE) \
           $(TARGET_OUT_ODM_DLKM) $(odm_dlkmimage_intermediates)/odm_dlkm_image_info.txt \
@@ -3430,23 +3386,29 @@
 
 # -----------------------------------------------------------------
 # Protected VM firmware image
-ifdef BOARD_PREBUILT_PVMFWIMAGE
+ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 INSTALLED_PVMFWIMAGE_TARGET := $(PRODUCT_OUT)/pvmfw.img
+INTERNAL_PREBUILT_PVMFWIMAGE := packages/modules/Virtualization/pvmfw/pvmfw.img
+
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+BUILT_PVMFWIMAGE_TARGET := $(BOARD_PREBUILT_PVMFWIMAGE)
+else ifeq ($(BUILDING_PVMFW_IMAGE),true)
+BUILT_PVMFWIMAGE_TARGET := $(INTERNAL_PREBUILT_PVMFWIMAGE)
+endif
 
 ifeq ($(BOARD_AVB_ENABLE),true)
-$(INSTALLED_PVMFWIMAGE_TARGET): $(BOARD_PREBUILT_PVMFWIMAGE) $(AVBTOOL) $(BOARD_AVB_PVMFW_KEY_PATH)
-	cp $(BOARD_PREBUILT_PVMFWIMAGE) $@
+$(INSTALLED_PVMFWIMAGE_TARGET): $(BUILT_PVMFWIMAGE_TARGET) $(AVBTOOL) $(BOARD_AVB_PVMFW_KEY_PATH)
+	cp $(BUILT_PVMFWIMAGE_TARGET) $@
 	$(AVBTOOL) add_hash_footer \
 	    --image $@ \
-	    --partition_size $(BOARD_PVMFWIMG_PARTITION_SIZE) \
+	    --partition_size $(BOARD_PVMFWIMAGE_PARTITION_SIZE) \
 	    --partition_name pvmfw $(INTERNAL_AVB_PVMFW_SIGNING_ARGS) \
 	    $(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)
 else
-$(INSTALLED_PVMFWIMAGE_TARGET): $(BOARD_PREBUILT_PVMFWIMAGE)
-	cp $(BOARD_PREBUILT_PVMFWIMAGE) $@
+$(eval $(call copy-one-file,$(BUILT_PVMFWIMAGE_TARGET),$(INSTALLED_PVMFWIMAGE_TARGET)))
 endif
 
-endif # BOARD_PREBUILT_PVMFWIMAGE
+endif # BOARD_USES_PVMFWIMAGE
 
 # Returns a list of image targets corresponding to the given list of partitions. For example, it
 # returns "$(INSTALLED_PRODUCTIMAGE_TARGET)" for "product", or "$(INSTALLED_SYSTEMIMAGE_TARGET)
@@ -4305,7 +4267,6 @@
 INTERNAL_OTATOOLS_MODULES := \
   aapt2 \
   add_img_to_target_files \
-  aftltool \
   apksigner \
   append2simg \
   avbtool \
@@ -4362,6 +4323,7 @@
   shflags \
   sign_apex \
   sign_target_files_apks \
+  sign_virt_apex \
   signapk \
   simg2img \
   sload_f2fs \
@@ -4373,6 +4335,7 @@
   verity_signer \
   verity_verifier \
   zipalign \
+  zucchini \
 
 # Additional tools to unpack and repack the apex file.
 INTERNAL_OTATOOLS_MODULES += \
@@ -4621,10 +4584,10 @@
 endif # BOARD_AVB_DTBO_KEY_PATH
 endif # BOARD_AVB_ENABLE
 endif # BOARD_PREBUILT_DTBOIMAGE
-ifdef BOARD_PREBUILT_PVMFWIMAGE
+ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 	$(hide) echo "has_pvmfw=true" >> $@
 ifeq ($(BOARD_AVB_ENABLE),true)
-	$(hide) echo "pvmfw_size=$(BOARD_PVMFWIMG_PARTITION_SIZE)" >> $@
+	$(hide) echo "pvmfw_size=$(BOARD_PVMFWIMAGE_PARTITION_SIZE)" >> $@
 	$(hide) echo "avb_pvmfw_add_hash_footer_args=$(BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS)" >> $@
 ifdef BOARD_AVB_PVMFW_KEY_PATH
 	$(hide) echo "avb_pvmfw_key_path=$(BOARD_AVB_PVMFW_KEY_PATH)" >> $@
@@ -4632,7 +4595,7 @@
 	$(hide) echo "avb_pvmfw_rollback_index_location=$(BOARD_AVB_PVMFW_ROLLBACK_INDEX_LOCATION)" >> $@
 endif # BOARD_AVB_PVMFW_KEY_PATH
 endif # BOARD_AVB_ENABLE
-endif # BOARD_PREBUILT_PVMFWIMAGE
+endif # BOARD_USES_PVMFWIMAGE
 	$(call dump-dynamic-partitions-info,$@)
 	@# VINTF checks
 ifeq ($(PRODUCT_ENFORCE_VINTF_MANIFEST),true)
@@ -4659,6 +4622,12 @@
 ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)
 	$(hide) echo "partial_ota_update_partitions_list=$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)" >> $@
 endif
+ifeq ($(BUILDING_WITH_VSDK),true)
+	$(hide) echo "building_with_vsdk=true" >> $@
+endif
+ifeq ($(TARGET_FLATTEN_APEX),false)
+	$(hide) echo "target_flatten_apex=false" >> $@
+endif
 
 .PHONY: misc_info
 misc_info: $(INSTALLED_MISC_INFO_TARGET)
@@ -4843,6 +4812,7 @@
 ifdef BUILDING_VENDOR_BOOT_IMAGE
   $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FILES)
   $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_RAMDISK_FRAGMENT_TARGETS)
+  $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)
   # The vendor ramdisk may be built from the recovery ramdisk.
   ifeq (true,$(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT))
     $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
@@ -5179,10 +5149,10 @@
 ifeq ($(AB_OTA_UPDATER),true)
 	@# When using the A/B updater, include the updater config files in the zip.
 	$(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt
-	$(hide) for part in $(AB_OTA_PARTITIONS); do \
+	$(hide) for part in $(strip $(AB_OTA_PARTITIONS)); do \
 	  echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \
 	done
-	$(hide) for conf in $(AB_OTA_POSTINSTALL_CONFIG); do \
+	$(hide) for conf in $(strip $(AB_OTA_POSTINSTALL_CONFIG)); do \
 	  echo "$${conf}" >> $(zip_root)/META/postinstall_config.txt; \
 	done
 ifdef OSRELEASED_DIRECTORY
@@ -5237,7 +5207,10 @@
 ifdef BOARD_PREBUILT_PVMFWIMAGE
 	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
 	$(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
-endif # BOARD_PREBUILT_PVMFWIMAGE
+else ifeq ($(BUILDING_PVMFW_IMAGE),true)
+	$(hide) mkdir -p $(zip_root)/IMAGES
+	$(hide) cp $(INSTALLED_PVMFWIMAGE_TARGET) $(zip_root)/IMAGES/
+endif
 ifdef BOARD_PREBUILT_BOOTLOADER
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_BOOTLOADER_MODULE) $(zip_root)/IMAGES/
@@ -5329,7 +5302,9 @@
 	@echo Package NDK sysroot...
 	$(hide) tar cjf $@ -C $(SOONG_OUT_DIR) ndk
 
+ifeq ($(HOST_OS),linux)
 $(call dist-for-goals,sdk,$(NDK_SYSROOT_TARGET))
+endif
 
 ifeq ($(build_ota_package),true)
 # -----------------------------------------------------------------
@@ -5434,25 +5409,34 @@
 endif # BUILD_OS == linux
 
 DEXPREOPT_CONFIG_ZIP := $(PRODUCT_OUT)/dexpreopt_config.zip
-$(DEXPREOPT_CONFIG_ZIP): $(FULL_SYSTEMIMAGE_DEPS) \
-	    $(INTERNAL_RAMDISK_FILES) \
-	    $(INTERNAL_USERDATAIMAGE_FILES) \
-	    $(INTERNAL_VENDORIMAGE_FILES) \
-	    $(INTERNAL_PRODUCTIMAGE_FILES) \
-	    $(INTERNAL_SYSTEM_EXTIMAGE_FILES) \
-	    $(DEX_PREOPT_CONFIG_FOR_MAKE) \
-	    $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)
+
+$(DEXPREOPT_CONFIG_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
+    $(INSTALLED_VENDORIMAGE_TARGET) \
+    $(INSTALLED_ODMIMAGE_TARGET) \
+    $(INSTALLED_PRODUCTIMAGE_TARGET) \
+
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
+$(DEXPREOPT_CONFIG_ZIP): $(DEX_PREOPT_CONFIG_FOR_MAKE) \
+	  $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) \
+
+endif
 
 $(DEXPREOPT_CONFIG_ZIP): $(SOONG_ZIP)
 	$(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/dexpreopt_config
+
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 ifneq (,$(DEX_PREOPT_CONFIG_FOR_MAKE))
 	$(hide) cp $(DEX_PREOPT_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config
 endif
 ifneq (,$(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE))
 	$(hide) cp $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(PRODUCT_OUT)/dexpreopt_config
 endif
+endif #!TARGET_BUILD_UNBUNDLED
 	$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/dexpreopt_config -D $(PRODUCT_OUT)/dexpreopt_config
 
+.PHONY: dexpreopt_config_zip
+dexpreopt_config_zip: $(DEXPREOPT_CONFIG_ZIP)
+
 # -----------------------------------------------------------------
 # A zip of the symbols directory.  Keep the full paths to make it
 # more obvious where these files came from.
@@ -5877,6 +5861,8 @@
 # -----------------------------------------------------------------
 # The SDK
 
+ifneq ($(filter sdk,$(MAKECMDGOALS)),)
+
 # The SDK includes host-specific components, so it belongs under HOST_OUT.
 sdk_dir := $(HOST_OUT)/sdk/$(TARGET_PRODUCT)
 
@@ -5886,15 +5872,11 @@
 #     darwin-x86  --> android-sdk_12345_mac-x86
 #     windows-x86 --> android-sdk_12345_windows
 #
+ifneq ($(HOST_OS),linux)
+  $(error Building the monolithic SDK is only supported on Linux)
+endif
 sdk_name := android-sdk_$(FILE_NAME_TAG)
-ifeq ($(HOST_OS),darwin)
-  INTERNAL_SDK_HOST_OS_NAME := mac
-else
-  INTERNAL_SDK_HOST_OS_NAME := $(HOST_OS)
-endif
-ifneq ($(HOST_OS),windows)
-  INTERNAL_SDK_HOST_OS_NAME := $(INTERNAL_SDK_HOST_OS_NAME)-$(SDK_HOST_ARCH)
-endif
+INTERNAL_SDK_HOST_OS_NAME := linux-$(SDK_HOST_ARCH)
 sdk_name := $(sdk_name)_$(INTERNAL_SDK_HOST_OS_NAME)
 
 sdk_dep_file := $(sdk_dir)/sdk_deps.mk
@@ -5914,9 +5896,7 @@
 atree_dir := development/build
 
 
-sdk_atree_files := \
-	$(atree_dir)/sdk.exclude.atree \
-	$(atree_dir)/sdk-$(HOST_OS)-$(SDK_HOST_ARCH).atree
+sdk_atree_files := $(atree_dir)/sdk.exclude.atree
 
 # development/build/sdk-android-<abi>.atree is used to differentiate
 # between architecture models (e.g. ARMv5TE versus ARMv7) when copying
@@ -5970,7 +5950,7 @@
 $(INTERNAL_SDK_TARGET): $(deps)
 	@echo "Package SDK: $@"
 	$(hide) rm -rf $(PRIVATE_DIR) $@
-	$(hide) for f in $(target_gnu_MODULES); do \
+	$(hide) for f in $(strip $(target_gnu_MODULES)); do \
 	  if [ -f $$f ]; then \
 	    echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \
 	        including GNU target $$f >&2; \
@@ -5998,22 +5978,16 @@
 	        -o $(PRIVATE_DIR) && \
 	    cp -f $(target_notice_file_txt) \
 	            $(PRIVATE_DIR)/system-images/android-$(PLATFORM_VERSION)/$(TARGET_CPU_ABI)/NOTICE.txt && \
-	    cp -f $(tools_notice_file_txt) $(PRIVATE_DIR)/platform-tools/NOTICE.txt && \
 	    HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \
 	        development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \
 	    chmod -R ug+rwX $(PRIVATE_DIR) && \
 	    cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \
 	) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 )
 
-
-# Is a Windows SDK requested? If so, we need some definitions from here
-# in order to find the Linux SDK used to create the Windows one.
-MAIN_SDK_NAME := $(sdk_name)
 MAIN_SDK_DIR  := $(sdk_dir)
 MAIN_SDK_ZIP  := $(INTERNAL_SDK_TARGET)
-ifneq ($(filter win_sdk winsdk-tools,$(MAKECMDGOALS)),)
-include $(TOPDIR)development/build/tools/windows_sdk.mk
-endif
+
+endif # sdk in MAKECMDGOALS
 
 # -----------------------------------------------------------------
 # Findbugs
diff --git a/core/OWNERS b/core/OWNERS
index 5456d4f..8794434 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -1,5 +1,5 @@
-per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,dbrazdil@google.com
-per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,skvadrik@google.com
+per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,skvadrik@google.com
 
 # For version updates
 per-file version_defaults.mk = aseaton@google.com,elisapascual@google.com,lubomir@google.com,pscovanner@google.com
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 5e63a25..4749694 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -28,6 +28,9 @@
 
 $(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
 $(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
+$(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT)
+$(call add_soong_config_var,ANDROID,BOARD_BUILD_SYSTEM_ROOT_IMAGE)
+$(call add_soong_config_var,ANDROID,PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
 
 ifeq (,$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES)))
   # Prebuilt module SDKs require prebuilt modules to work, and currently
@@ -42,7 +45,9 @@
   $(call add_soong_config_namespace,art_module)
   SOONG_CONFIG_art_module += source_build
 endif
-ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS)))
+ifneq (,$(SOONG_CONFIG_art_module_source_build))
+  # Keep an explicit setting.
+else ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS)))
   # Build ART modules from source if they are listed in TARGET_BUILD_APPS.
   SOONG_CONFIG_art_module_source_build := true
 else ifeq (,$(filter-out modules_% mainline_modules_%,$(TARGET_PRODUCT)))
@@ -61,10 +66,6 @@
   # Prebuilts aren't built with sanitizers either.
   SOONG_CONFIG_art_module_source_build := true
   MODULE_BUILD_FROM_SOURCE := true
-else ifneq (,$(PRODUCT_FUCHSIA))
-  # Fuchsia picks out ART internal packages that aren't available in the
-  # prebuilt.
-  SOONG_CONFIG_art_module_source_build := true
 else ifeq (,$(filter x86 x86_64,$(HOST_CROSS_ARCH)))
   # We currently only provide prebuilts for x86 on host. This skips prebuilts in
   # cuttlefish builds for ARM servers.
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 1b7a279..ceb69bb 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -373,7 +373,13 @@
 
 LOCAL_BUILT_MODULE := $(intermediates)/$(my_built_module_stem)
 
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+  ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+    $(call pretty-error, LOCAL_SOONG_INSTALLED_MODULE can only be used from $(SOONG_ANDROID_MK))
+  endif
+  # Use the install path requested by Soong.
+  LOCAL_INSTALLED_MODULE := $(LOCAL_SOONG_INSTALLED_MODULE)
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
   # Apk and its attachments reside in its own subdir.
   ifeq ($(LOCAL_MODULE_CLASS),APPS)
     # framework-res.apk doesn't like the additional layer.
@@ -507,9 +513,6 @@
 ## Module installation rule
 ###########################################################
 
-my_init_rc_installed :=
-my_init_rc_path :=
-my_init_rc_pairs :=
 my_installed_symlinks :=
 my_default_test_module :=
 ifeq ($(use_testcase_folder),true)
@@ -518,80 +521,89 @@
 arch_dir :=
 endif
 
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module))
-$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
-$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+  # Soong already generated the copy rule, but make the installed location depend on the Make
+  # copy of the intermediates for now, as some rules that collect intermediates may expect
+  # them to exist.
+  $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+  ifneq ($(LOCAL_INSTALLED_MODULE),$(my_default_test_module))
+    $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
+    $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
 	@echo "Install: $@"
-ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
+    ifeq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK))
 	$(copy-file-or-link-to-new-target)
-else
+    else
 	$(copy-file-to-new-target)
-endif
+    endif
 	$(PRIVATE_POST_INSTALL_CMD)
-endif
+  endif
 
-ifndef LOCAL_IS_HOST_MODULE
-# Rule to install the module's companion init.rc.
-ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)
-my_init_rc := $(LOCAL_FULL_INIT_RC)
-else
-my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))
-endif
-ifneq ($(strip $(my_init_rc)),)
-# Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
-# have init.rc files that need to be installed alongside them. Manually handle the case where the
-# output file is in the recovery partition.
-my_init_rc_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
-my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))
-my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+  # Rule to install the module's companion symlinks
+  my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
+  $(foreach symlink,$(my_installed_symlinks),\
+      $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink)))
 
-# Make sure we only set up the copy rules once, even if another arch variant
-# shares a common LOCAL_INIT_RC.
-my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
-my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))
-ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
-
-$(my_all_targets) : $(my_init_rc_installed)
-endif # my_init_rc
-endif # !LOCAL_IS_HOST_MODULE
-
-# Rule to install the module's companion symlinks
-my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
-$(foreach symlink,$(my_installed_symlinks),\
-    $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink)))
-
-$(my_all_targets) : | $(my_installed_symlinks)
+  $(my_all_targets) : | $(my_installed_symlinks)
 
 endif # !LOCAL_UNINSTALLABLE_MODULE
 
 ###########################################################
-## VINTF manifest fragment goals
+## VINTF manifest fragment and init.rc goals
 ###########################################################
 
 my_vintf_installed:=
 my_vintf_pairs:=
+my_init_rc_installed :=
+my_init_rc_path :=
+my_init_rc_pairs :=
 ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ifndef LOCAL_IS_HOST_MODULE
-ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),)
-my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS)
-else
-my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml))
-endif
-ifneq ($(strip $(my_vintf_fragments)),)
+  ifndef LOCAL_IS_HOST_MODULE
+    # Rule to install the module's companion vintf fragments.
+    ifneq ($(strip $(LOCAL_FULL_VINTF_FRAGMENTS)),)
+      my_vintf_fragments := $(LOCAL_FULL_VINTF_FRAGMENTS)
+    else
+      my_vintf_fragments := $(foreach xml,$(LOCAL_VINTF_FRAGMENTS),$(LOCAL_PATH)/$(xml))
+    endif
+    ifneq ($(strip $(my_vintf_fragments)),)
 
-my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(TARGET_OUT$(partition_tag)_ETC)/vintf/manifest/$(notdir $(xml)))
-my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml)))
+      my_vintf_pairs := $(foreach xml,$(my_vintf_fragments),$(xml):$(TARGET_OUT$(partition_tag)_ETC)/vintf/manifest/$(notdir $(xml)))
+      my_vintf_installed := $(foreach xml,$(my_vintf_pairs),$(call word-colon,2,$(xml)))
 
-# Only set up copy rules once, even if another arch variant shares it
-my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs))
-my_vintf_new_installed := $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs))
+      # Only set up copy rules once, even if another arch variant shares it
+      my_vintf_new_pairs := $(filter-out $(ALL_VINTF_MANIFEST_FRAGMENTS_LIST),$(my_vintf_pairs))
+      my_vintf_new_installed := $(call copy-many-vintf-manifest-files-checked,$(my_vintf_new_pairs))
 
-ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)
+      ALL_VINTF_MANIFEST_FRAGMENTS_LIST += $(my_vintf_new_pairs)
 
-$(my_all_targets) : $(my_vintf_new_installed)
-endif # LOCAL_VINTF_FRAGMENTS
-endif # !LOCAL_IS_HOST_MODULE
+      $(my_all_targets) : $(my_vintf_new_installed)
+    endif # my_vintf_fragments
+
+    # Rule to install the module's companion init.rc.
+    ifneq ($(strip $(LOCAL_FULL_INIT_RC)),)
+      my_init_rc := $(LOCAL_FULL_INIT_RC)
+    else
+      my_init_rc := $(foreach rc,$(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC),$(LOCAL_PATH)/$(rc))
+    endif
+    ifneq ($(strip $(my_init_rc)),)
+      # Make doesn't support recovery as an output partition, but some Soong modules installed in recovery
+      # have init.rc files that need to be installed alongside them. Manually handle the case where the
+      # output file is in the recovery partition.
+      my_init_rc_path := $(if $(filter $(TARGET_RECOVERY_ROOT_OUT)/%,$(my_module_path)),$(TARGET_RECOVERY_ROOT_OUT)/system/etc,$(TARGET_OUT$(partition_tag)_ETC))
+      my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(rc):$(my_init_rc_path)/init/$(notdir $(rc)))
+      my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+
+      # Make sure we only set up the copy rules once, even if another arch variant
+      # shares a common LOCAL_INIT_RC.
+      my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
+      my_init_rc_new_installed := $(call copy-many-init-script-files-checked,$(my_init_rc_new_pairs))
+
+      ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
+
+      $(my_all_targets) : $(my_init_rc_installed)
+    endif # my_init_rc
+
+  endif # !LOCAL_IS_HOST_MODULE
 endif # !LOCAL_UNINSTALLABLE_MODULE
 
 ###########################################################
@@ -902,12 +914,34 @@
 ALL_MODULES.$(my_register_name).TARGET_BUILT := \
     $(ALL_MODULES.$(my_register_name).TARGET_BUILT) $(LOCAL_BUILT_MODULE)
 endif
-ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
-ALL_MODULES.$(my_register_name).INSTALLED := \
+ifneq (,$(LOCAL_SOONG_INSTALLED_MODULE))
+  # Store the list of paths to installed locations of files provided by this
+  # module.  Used as dependencies of the image packaging rules when the module
+  # is installed by the current product.
+  ALL_MODULES.$(my_register_name).INSTALLED := \
+    $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
+      $(foreach f, $(LOCAL_SOONG_INSTALL_PAIRS),\
+        $(word 2,$(subst :,$(space),$(f)))) \
+      $(LOCAL_SOONG_INSTALL_SYMLINKS) \
+      $(my_init_rc_installed) \
+      $(my_installed_test_data) \
+      $(my_vintf_installed))
+  # Store the list of colon-separated pairs of the built and installed locations
+  # of files provided by this module.  Used by custom packaging rules like
+  # package-modules.mk that need to copy the built files to a custom install
+  # location during packaging.
+  ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
+    $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
+      $(LOCAL_SOONG_INSTALL_PAIRS) \
+      $(my_init_rc_pairs) \
+      $(my_test_data_pairs) \
+      $(my_vintf_pairs))
+else ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+  ALL_MODULES.$(my_register_name).INSTALLED := \
     $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
     $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks) \
     $(my_installed_test_data) $(my_vintf_installed))
-ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
+  ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
     $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
     $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \
     $(my_init_rc_pairs) $(my_test_data_pairs) $(my_vintf_pairs))
diff --git a/core/board_config.mk b/core/board_config.mk
index 1b08f9a..40b6b3f 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -84,6 +84,7 @@
 _board_strip_readonly_list += BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE
 _board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_PARTITION_SIZE
 _board_strip_readonly_list += BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE
+_board_strip_readonly_list += BOARD_PVMFWIMAGE_PARTITION_SIZE
 
 # Logical partitions related variables.
 _board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE
@@ -184,7 +185,20 @@
   .KATI_READONLY := TARGET_DEVICE_DIR
 endif
 
+# TODO(colefaust) change this if to RBC_PRODUCT_CONFIG when
+# the board configuration is known to work on everything
+# the product config works on.
+ifndef RBC_BOARD_CONFIG
 include $(board_config_mk)
+else
+  rc := $(shell build/soong/scripts/rbc-run $(board_config_mk) \
+      BUILDING_GSI=$(BUILDING_GSI) >$(OUT_DIR)/rbcboardtemp.mk || echo $$?)
+  ifneq (,$(rc))
+    $(error board configuration converter failed: $(rc))
+  endif
+
+  include $(OUT_DIR)/rbcboardtemp.mk
+endif
 
 ifneq (,$(and $(TARGET_ARCH),$(TARGET_ARCH_SUITE)))
   $(error $(board_config_mk) erroneously sets both TARGET_ARCH and TARGET_ARCH_SUITE)
@@ -439,6 +453,86 @@
 endif
 .KATI_READONLY := BUILDING_RAMDISK_IMAGE
 
+# Are we building a debug vendor_boot image
+BUILDING_DEBUG_VENDOR_BOOT_IMAGE :=
+# Can't build vendor_boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true,
+# because building debug vendor_boot image requires a ramdisk.
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \
+      Skip building the debug vendor_boot image.)
+  endif
+# Can't build vendor_boot-debug.img if we're not building a ramdisk.
+else ifndef BUILDING_RAMDISK_IMAGE
+  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a ramdisk image. \
+      Skip building the debug vendor_boot image.)
+  endif
+# Can't build vendor_boot-debug.img if we're not building a vendor_boot.img.
+else ifndef BUILDING_VENDOR_BOOT_IMAGE
+  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE is true, but we're not building a vendor_boot image. \
+      Skip building the debug vendor_boot image.)
+  endif
+else
+  ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),)
+    BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true
+  else ifeq ($(PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE),true)
+    BUILDING_DEBUG_VENDOR_BOOT_IMAGE := true
+  endif
+endif
+.KATI_READONLY := BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+
+_has_boot_img_artifact :=
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
+  ifdef BUILDING_BOOT_IMAGE
+    _has_boot_img_artifact := true
+  endif
+  # BUILDING_RECOVERY_IMAGE && BOARD_USES_RECOVERY_AS_BOOT implies that
+  # recovery is being built with the file name *boot.img*, which still counts
+  # as "building boot.img".
+  ifdef BUILDING_RECOVERY_IMAGE
+    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+      _has_boot_img_artifact := true
+    endif
+  endif
+endif
+
+# Are we building a debug boot image
+BUILDING_DEBUG_BOOT_IMAGE :=
+# Can't build boot-debug.img if BOARD_BUILD_SYSTEM_ROOT_IMAGE is true,
+# because building debug boot image requires a ramdisk.
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but so is BOARD_BUILD_SYSTEM_ROOT_IMAGE. \
+      Skip building the debug boot image.)
+  endif
+# Can't build boot-debug.img if we're not building a ramdisk.
+else ifndef BUILDING_RAMDISK_IMAGE
+  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a ramdisk image. \
+      Skip building the debug boot image.)
+  endif
+# Can't build boot-debug.img if we're not building a boot.img.
+else ifndef _has_boot_img_artifact
+  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \
+      Skip building the debug boot image.)
+  endif
+else
+  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),)
+    BUILDING_DEBUG_BOOT_IMAGE := true
+    # Don't build boot-debug.img if we're already building vendor_boot-debug.img.
+    ifdef BUILDING_DEBUG_VENDOR_BOOT_IMAGE
+      BUILDING_DEBUG_BOOT_IMAGE :=
+    endif
+  else ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+    BUILDING_DEBUG_BOOT_IMAGE := true
+  endif
+endif
+.KATI_READONLY := BUILDING_DEBUG_BOOT_IMAGE
+_has_boot_img_artifact :=
+
 # Are we building a userdata image
 BUILDING_USERDATA_IMAGE :=
 ifeq ($(PRODUCT_BUILD_USERDATA_IMAGE),)
@@ -695,6 +789,24 @@
 endif
 .KATI_READONLY := BUILDING_ODM_DLKM_IMAGE
 
+BOARD_USES_PVMFWIMAGE :=
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+  BOARD_USES_PVMFWIMAGE := true
+endif
+ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true)
+  BOARD_USES_PVMFWIMAGE := true
+endif
+.KATI_READONLY := BOARD_USES_PVMFWIMAGE
+
+BUILDING_PVMFW_IMAGE :=
+ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true)
+  BUILDING_PVMFW_IMAGE := true
+endif
+ifdef BOARD_PREBUILT_PVMFWIMAGE
+  BUILDING_PVMFW_IMAGE :=
+endif
+.KATI_READONLY := BUILDING_PVMFW_IMAGE
+
 ###########################################
 # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.
 TARGET_RECOVERY_UPDATER_LIBS ?=
@@ -745,7 +857,7 @@
 
 ifdef BOARD_VNDK_VERSION
   ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
-    $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current))
+    $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
   endif
   ifneq ($(BOARD_VNDK_VERSION),current)
     $(call check_vndk_version,$(BOARD_VNDK_VERSION))
@@ -766,8 +878,8 @@
 endif
 
 ###########################################
-# APEXes are by default flattened, i.e. non-updatable.
-# It can be unflattened (and updatable) by inheriting from
+# APEXes are by default flattened, i.e. non-updatable, if not building unbundled
+# apps. It can be unflattened (and updatable) by inheriting from
 # updatable_apex.mk
 #
 # APEX flattening can also be forcibly enabled (resp. disabled) by
@@ -776,7 +888,7 @@
 ifdef OVERRIDE_TARGET_FLATTEN_APEX
   TARGET_FLATTEN_APEX := $(OVERRIDE_TARGET_FLATTEN_APEX)
 else
-  ifeq (,$(TARGET_FLATTEN_APEX))
+  ifeq (,$(TARGET_BUILD_APPS)$(TARGET_FLATTEN_APEX))
     TARGET_FLATTEN_APEX := true
   endif
 endif
@@ -803,8 +915,8 @@
     $(KATI_deprecated_var $(m),Please convert to Soong)))
 
 $(if $(filter true,$(BUILD_BROKEN_USES_BUILD_COPY_HEADERS)),\
-  $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)#copy_headers),\
-  $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)#copy_headers))
+  $(KATI_deprecated_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers),\
+  $(KATI_obsolete_var BUILD_COPY_HEADERS,See $(CHANGES_URL)\#copy_headers))
 
 $(foreach m,$(filter-out BUILD_COPY_HEADERS,$(DEFAULT_ERROR_BUILD_MODULE_TYPES)),\
   $(if $(filter true,$(BUILD_BROKEN_USES_$(m))),\
diff --git a/core/build_id.mk b/core/build_id.mk
index eee184e..2f9c3f3 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -18,4 +18,4 @@
 # (like "CRB01").  It must be a single word, and is
 # capitalized by convention.
 
-BUILD_ID=SC_V2
+BUILD_ID=MASTER
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 94a027c..887c047 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -42,6 +42,7 @@
 LOCAL_CLANG_LDFLAGS:=
 LOCAL_CLASSPATH:=
 LOCAL_COMPATIBILITY_SUITE:=
+LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY:=
 LOCAL_COMPATIBILITY_SUPPORT_FILES:=
 LOCAL_COMPRESSED_MODULE:=
 LOCAL_CONLYFLAGS:=
@@ -282,6 +283,9 @@
 LOCAL_SOONG_DEXPREOPT_CONFIG :=
 LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=
 LOCAL_SOONG_HEADER_JAR :=
+LOCAL_SOONG_INSTALL_PAIRS :=
+LOCAL_SOONG_INSTALL_SYMLINKS :=
+LOCAL_SOONG_INSTALLED_MODULE :=
 LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=
 LOCAL_SOONG_LINK_TYPE :=
 LOCAL_SOONG_LINT_REPORTS :=
diff --git a/core/config.mk b/core/config.mk
index 93c5db1..8f47ab5 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -252,6 +252,10 @@
 # Initialize SOONG_CONFIG_NAMESPACES so that it isn't recursive.
 SOONG_CONFIG_NAMESPACES :=
 
+# TODO(asmundak): remove add_soong_config_namespace, add_soong_config_var,
+# and add_soong_config_var_value once all their usages are replaced with
+# soong_config_set/soong_config_append.
+
 # The add_soong_config_namespace function adds a namespace and initializes it
 # to be empty.
 # $1 is the namespace.
@@ -259,7 +263,7 @@
 
 define add_soong_config_namespace
 $(eval SOONG_CONFIG_NAMESPACES += $1) \
-$(eval SOONG_CONFIG_$1 :=)
+$(eval SOONG_CONFIG_$(strip $1) :=)
 endef
 
 # The add_soong_config_var function adds a a list of soong config variables to
@@ -268,8 +272,8 @@
 # $1 is the namespace. $2 is the list of variables.
 # Ex: $(call add_soong_config_var,acme,COOL_FEATURE_A COOL_FEATURE_B)
 define add_soong_config_var
-$(eval SOONG_CONFIG_$1 += $2) \
-$(foreach v,$2,$(eval SOONG_CONFIG_$1_$v := $($v)))
+$(eval SOONG_CONFIG_$(strip $1) += $2) \
+$(foreach v,$(strip $2),$(eval SOONG_CONFIG_$(strip $1)_$v := $($v)))
 endef
 
 # The add_soong_config_var_value function defines a make variable and also adds
@@ -282,6 +286,32 @@
 $(call add_soong_config_var,$1,$2)
 endef
 
+# Soong config namespace variables manipulation.
+#
+# internal utility to define a namespace and a variable in it.
+define soong_config_define_internal
+$(if $(filter $1,$(SOONG_CONFIG_NAMESPACES)),,$(eval SOONG_CONFIG_NAMESPACES:=$(SOONG_CONFIG_NAMESPACES) $1)) \
+$(if $(filter $2,$(SOONG_CONFIG_$(strip $1))),,$(eval SOONG_CONFIG_$(strip $1):=$(SOONG_CONFIG_$(strip $1)) $2))
+endef
+
+# soong_config_set defines the variable in the given Soong config namespace
+# and sets its value. If the namespace does not exist, it will be defined.
+# $1 is the namespace. $2 is the variable name. $3 is the variable value.
+# Ex: $(call soong_config_set,acme,COOL_FEATURE,true)
+define soong_config_set
+$(call soong_config_define_internal,$1,$2) \
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$3)
+endef
+
+# soong_config_append appends to the value of the variable in the given Soong
+# config namespace. If the varabile does not exist, it will be defined. If the
+# namespace does not  exist, it will be defined.
+# $1 is the namespace, $2 is the variable name, $3 is the value
+define soong_config_append
+$(call soong_config_define_internal,$1,$2) \
+$(eval SOONG_CONFIG_$(strip $1)_$(strip $2):=$(SOONG_CONFIG_$(strip $1)_$(strip $2)) $3)
+endef
+
 # Set the extensions used for various packages
 COMMON_PACKAGE_SUFFIX := .zip
 COMMON_JAVA_PACKAGE_SUFFIX := .jar
@@ -445,6 +475,11 @@
 ifneq ($(filter true,$(SOONG_ALLOW_MISSING_DEPENDENCIES)),)
 ALLOW_MISSING_DEPENDENCIES := true
 endif
+# Mac builds default to ALLOW_MISSING_DEPENDENCIES, at least until the host
+# tools aren't enabled by default for Mac.
+ifeq ($(HOST_OS),darwin)
+  ALLOW_MISSING_DEPENDENCIES := true
+endif
 .KATI_READONLY := ALLOW_MISSING_DEPENDENCIES
 
 TARGET_BUILD_USE_PREBUILT_SDKS :=
@@ -485,11 +520,8 @@
 #
 ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))
   AAPT := $(HOST_OUT_EXECUTABLES)/aapt
-  MAINDEXCLASSES := $(HOST_OUT_EXECUTABLES)/mainDexClasses
-
 else # TARGET_BUILD_USE_PREBUILT_SDKS
   AAPT := $(prebuilt_sdk_tools_bin)/aapt
-  MAINDEXCLASSES := $(prebuilt_sdk_tools)/mainDexClasses
 endif # TARGET_BUILD_USE_PREBUILT_SDKS
 
 ifeq (,$(TARGET_BUILD_USE_PREBUILT_SDKS))
@@ -603,7 +635,7 @@
 # Path to tools.jar
 HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar
 
-APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner --compatible-output=no
+APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner
 
 # Boolean variable determining if the allow list for compatible properties is enabled
 PRODUCT_COMPATIBLE_PROPERTY := true
@@ -721,10 +753,13 @@
 endif
 .KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES
 
+min_systemsdk_version := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL) $(PRODUCT_SHIPPING_API_LEVEL))
+ifneq (,$(min_systemsdk_version))
+ifneq ($(call numbers_less_than,$(min_systemsdk_version),$(BOARD_SYSTEMSDK_VERSIONS)),)
+  $(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to BOARD_API_LEVEL, BOARD_SHIPPING_API_LEVEL or PRODUCT_SHIPPING_API_LEVEL ($(min_systemsdk_version)))
+endif
+endif
 ifdef PRODUCT_SHIPPING_API_LEVEL
-  ifneq ($(call numbers_less_than,$(PRODUCT_SHIPPING_API_LEVEL),$(BOARD_SYSTEMSDK_VERSIONS)),)
-    $(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to PRODUCT_SHIPPING_API_LEVEL ($(PRODUCT_SHIPPING_API_LEVEL)))
-  endif
   ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),28),)
     ifneq ($(TARGET_IS_64_BIT), true)
       ifneq ($(TARGET_USES_64_BIT_BINDER), true)
@@ -792,6 +827,7 @@
     28.0 \
     29.0 \
     30.0 \
+    31.0 \
 
 .KATI_READONLY := \
     PLATFORM_SEPOLICY_COMPAT_VERSIONS \
@@ -998,6 +1034,14 @@
 BOARD_PREBUILT_HIDDENAPI_DIR ?=
 .KATI_READONLY := BOARD_PREBUILT_HIDDENAPI_DIR
 
+ifdef USE_HOST_MUSL
+  ifneq (,$(or $(BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE),\
+               $(BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY),\
+               $(BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY)))
+    $(error USE_HOST_MUSL can't be set when native host builds are enabled in Make with BUILD_BROKEN_USES_BUILD_HOST_*)
+  endif
+endif
+
 # ###############################################################
 # Set up final options.
 # ###############################################################
diff --git a/core/definitions.mk b/core/definitions.mk
index c5fe76b..c7172ca 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -598,7 +598,7 @@
 $(_dir)/$(1).meta_lic : $(_deps) $(_notices) $(foreach b,$(_tgts), $(_dir)/$(b).meta_module) build/make/tools/build-license-metadata.sh
 	rm -f $$@
 	mkdir -p $$(dir $$@)
-	build/make/tools/build-license-metadata.sh -k $$(PRIVATE_KINDS) -c $$(PRIVATE_CONDITIONS) -n $$(PRIVATE_NOTICES) -d $$(PRIVATE_NOTICE_DEPS) -m $$(PRIVATE_INSTALL_MAP) -t $$(PRIVATE_TARGETS) $$(if $$(PRIVATE_IS_CONTAINER),-is_container) -p $$(PRIVATE_PACKAGE_NAME) -o $$@
+	build/make/tools/build-license-metadata.sh -k $$(PRIVATE_KINDS) -c $$(PRIVATE_CONDITIONS) -n $$(PRIVATE_NOTICES) -d $$(PRIVATE_NOTICE_DEPS) -m $$(PRIVATE_INSTALL_MAP) -t $$(PRIVATE_TARGETS) $$(if $$(PRIVATE_IS_CONTAINER),-is_container) -p '$$(PRIVATE_PACKAGE_NAME)' -o $$@
 
 .PHONY: $(1).meta_lic
 $(1).meta_lic : $(_dir)/$(1).meta_lic
@@ -1106,11 +1106,11 @@
 $(hide) mkdir -p $(dir $@)
 $(hide) $(BCC_COMPAT) -O3 -o $(dir $@)/$(notdir $(<:.bc=.o)) -fPIC -shared \
   -rt-path $(RS_PREBUILT_CLCORE) -mtriple $(RS_COMPAT_TRIPLE) $<
-$(hide) $(PRIVATE_CXX_LINK) -shared -Wl,-soname,$(notdir $@) -nostdlib \
+$(hide) $(PRIVATE_CXX_LINK) -fuse-ld=lld -target $(CLANG_TARGET_TRIPLE) -shared -Wl,-soname,$(notdir $@) -nostdlib \
   -Wl,-rpath,\$$ORIGIN/../lib \
   $(dir $@)/$(notdir $(<:.bc=.o)) \
   $(RS_PREBUILT_COMPILER_RT) \
-  -o $@ $(CLANG_TARGET_GLOBAL_LDFLAGS) -Wl,--hash-style=sysv \
+  -o $@ $(CLANG_TARGET_GLOBAL_LLDFLAGS) -Wl,--hash-style=sysv \
   -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib64 \
   -L $(SOONG_OUT_DIR)/ndk/platforms/android-$(PRIVATE_SDK_VERSION)/arch-$(TARGET_ARCH)/usr/lib \
   $(call intermediates-dir-for,SHARED_LIBRARIES,libRSSupport)/libRSSupport.so \
@@ -1928,21 +1928,10 @@
 # b/37750224
 AAPT_ASAN_OPTIONS := ASAN_OPTIONS=detect_leaks=0
 
-# Search for generated R.java/Manifest.java in $1, copy the found R.java as $2.
-# Also copy them to a central 'R' directory to make it easier to add the files to an IDE.
+# Search for generated R.java in $1, copy the found R.java as $2.
 define find-generated-R.java
-$(hide) for GENERATED_MANIFEST_FILE in `find $(1) \
-  -name Manifest.java 2> /dev/null`; do \
-    dir=`awk '/package/{gsub(/\./,"/",$$2);gsub(/;/,"",$$2);print $$2;exit}' $$GENERATED_MANIFEST_FILE`; \
-    mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
-    cp $$GENERATED_MANIFEST_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
-  done;
 $(hide) for GENERATED_R_FILE in `find $(1) \
   -name R.java 2> /dev/null`; do \
-    dir=`awk '/package/{gsub(/\./,"/",$$2);gsub(/;/,"",$$2);print $$2;exit}' $$GENERATED_R_FILE`; \
-    mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
-    cp $$GENERATED_R_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir \
-      || exit 31; \
     cp $$GENERATED_R_FILE $(2) || exit 32; \
   done;
 @# Ensure that the target file is always created, i.e. also in case we did not
@@ -2340,6 +2329,7 @@
 define add-jar-resources-to-package
   rm -rf $(3)
   mkdir -p $(3)
+  zipinfo -1 $(2) > /dev/null
   unzip -qo $(2) -d $(3) $$(zipinfo -1 $(2) | grep -v -E "\.class$$")
   $(JAR) uf $(1) $(call jar-args-sorted-files-in-directory,$(3))
 endef
@@ -3004,9 +2994,10 @@
 # Can be passed a subdirectory to use for the common testcase directory.
 define compatibility_suite_dirs
   $(strip \
-    $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1)),\
-      $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\
-      $(COMPATIBILITY_TESTCASES_OUT_$(1))) \
+    $(if $(COMPATIBILITY_TESTCASES_OUT_$(1)), \
+      $(if $(COMPATIBILITY_TESTCASES_OUT_INCLUDE_MODULE_FOLDER_$(1))$(LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY),\
+        $(COMPATIBILITY_TESTCASES_OUT_$(1))/$(LOCAL_MODULE)$(2),\
+        $(COMPATIBILITY_TESTCASES_OUT_$(1)))) \
     $($(my_prefix)OUT_TESTCASES)/$(LOCAL_MODULE)$(2))
 endef
 
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 51238a3..0c806c1 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -104,11 +104,11 @@
   $(call add_json_bool, DisableGenerateProfile,                  $(filter false,$(WITH_DEX_PREOPT_GENERATE_PROFILE)))
   $(call add_json_str,  ProfileDir,                              $(PRODUCT_DEX_PREOPT_PROFILE_DIR))
   $(call add_json_list, BootJars,                                $(PRODUCT_BOOT_JARS))
-  $(call add_json_list, UpdatableBootJars,                       $(PRODUCT_UPDATABLE_BOOT_JARS))
+  $(call add_json_list, ApexBootJars,                            $(PRODUCT_APEX_BOOT_JARS))
   $(call add_json_list, ArtApexJars,                             $(filter $(PRODUCT_BOOT_JARS),$(ART_APEX_JARS)))
   $(call add_json_list, SystemServerJars,                        $(PRODUCT_SYSTEM_SERVER_JARS))
   $(call add_json_list, SystemServerApps,                        $(PRODUCT_SYSTEM_SERVER_APPS))
-  $(call add_json_list, UpdatableSystemServerJars,               $(PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS))
+  $(call add_json_list, ApexSystemServerJars,                    $(PRODUCT_APEX_SYSTEM_SERVER_JARS))
   $(call add_json_bool, BrokenSuboptimalOrderOfSystemServerJars, $(PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS))
   $(call add_json_list, SpeedApps,                               $(PRODUCT_DEXPREOPT_SPEED_APPS))
   $(call add_json_list, PreoptFlags,                             $(PRODUCT_DEX_PREOPT_DEFAULT_FLAGS))
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index a2837f3..ea50313 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -60,11 +60,6 @@
   LOCAL_DEX_PREOPT :=
 endif
 
-# Don't preopt system server jars that are updatable.
-ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS)))
-  LOCAL_DEX_PREOPT :=
-endif
-
 # if WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true and module is not in boot class path skip
 # Also preopt system server jars since selinux prevents system server from loading anything from
 # /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
@@ -278,6 +273,7 @@
 my_dexpreopt_image_locations_on_host :=
 my_dexpreopt_image_locations_on_device :=
 my_dexpreopt_infix := boot
+my_create_dexpreopt_config :=
 ifeq (true, $(DEXPREOPT_USE_ART_IMAGE))
   my_dexpreopt_infix := art
 endif
@@ -293,7 +289,16 @@
       LOCAL_UNCOMPRESS_DEX := true
     endif
   endif
+  my_create_dexpreopt_config := true
+endif
 
+# dexpreopt is disabled when TARGET_BUILD_UNBUNDLED_IMAGE is true,
+# but dexpreopt config files are required to dexpreopt in post-processing.
+ifeq ($(TARGET_BUILD_UNBUNDLED_IMAGE),true)
+  my_create_dexpreopt_config := true
+endif
+
+ifeq ($(my_create_dexpreopt_config), true)
   ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
     my_module_multilib := $(LOCAL_MULTILIB)
     # If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
@@ -402,8 +407,6 @@
 
   my_dexpreopt_config := $(intermediates)/dexpreopt.config
   my_dexpreopt_config_for_postprocessing := $(PRODUCT_OUT)/dexpreopt_config/$(LOCAL_MODULE)_dexpreopt.config
-  my_dexpreopt_script := $(intermediates)/dexpreopt.sh
-  my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
   my_dexpreopt_config_merger := $(BUILD_SYSTEM)/dex_preopt_config_merger.py
 
   $(my_dexpreopt_config): $(my_dexpreopt_dep_configs) $(my_dexpreopt_config_merger)
@@ -416,12 +419,39 @@
 	echo -e -n '$(subst $(newline),\n,$(subst ','\'',$(subst \,\\,$(PRIVATE_CONTENTS))))' > $@
 	$(PRIVATE_CONFIG_MERGER) $@ $(PRIVATE_DEP_CONFIGS)
 
+$(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing)))
+
+$(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing)
+
+# System server jars defined in Android.mk are deprecated.
+ifneq (true, $(PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS))
+  ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS) $(PRODUCT_APEX_SYSTEM_SERVER_JARS)))
+    $(error System server jars defined in Android.mk are deprecated. \
+      Convert $(LOCAL_MODULE) to Android.bp or temporarily disable the error \
+      with 'PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS := true')
+  endif
+endif
+
+ifdef LOCAL_DEX_PREOPT
+  # System server jars must be copied into predefined locations expected by
+  # dexpreopt. Copy rule must be exposed to Ninja (as it uses these files as
+  # inputs), so it cannot go in dexpreopt.sh.
+  ifneq (,$(filter %:$(LOCAL_MODULE), $(PRODUCT_SYSTEM_SERVER_JARS)))
+    my_dexpreopt_jar_copy := $(OUT_DIR)/soong/system_server_dexjars/$(LOCAL_MODULE).jar
+    $(my_dexpreopt_jar_copy): PRIVATE_BUILT_MODULE := $(LOCAL_BUILT_MODULE)
+    $(my_dexpreopt_jar_copy): $(LOCAL_BUILT_MODULE)
+	  @cp $(PRIVATE_BUILT_MODULE) $@
+  endif
+
+  my_dexpreopt_script := $(intermediates)/dexpreopt.sh
+  my_dexpreopt_zip := $(intermediates)/dexpreopt.zip
   .KATI_RESTAT: $(my_dexpreopt_script)
   $(my_dexpreopt_script): PRIVATE_MODULE := $(LOCAL_MODULE)
   $(my_dexpreopt_script): PRIVATE_GLOBAL_SOONG_CONFIG := $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE)
   $(my_dexpreopt_script): PRIVATE_GLOBAL_CONFIG := $(DEX_PREOPT_CONFIG_FOR_MAKE)
   $(my_dexpreopt_script): PRIVATE_MODULE_CONFIG := $(my_dexpreopt_config)
   $(my_dexpreopt_script): $(DEXPREOPT_GEN)
+  $(my_dexpreopt_script): $(my_dexpreopt_jar_copy)
   $(my_dexpreopt_script): $(my_dexpreopt_config) $(DEX_PREOPT_SOONG_CONFIG_FOR_MAKE) $(DEX_PREOPT_CONFIG_FOR_MAKE)
 	@echo "$(PRIVATE_MODULE) dexpreopt gen"
 	$(DEXPREOPT_GEN) \
@@ -431,8 +461,6 @@
 	-dexpreopt_script $@ \
 	-out_dir $(OUT_DIR)
 
-  $(eval $(call copy-one-file,$(my_dexpreopt_config),$(my_dexpreopt_config_for_postprocessing)))
-
   my_dexpreopt_deps := $(my_dex_jar)
   my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))
   my_dexpreopt_deps += \
@@ -468,7 +496,6 @@
 
   $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
   $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_zip)
-  $(LOCAL_INSTALLED_MODULE): $(my_dexpreopt_config_for_postprocessing)
 
   $(my_all_targets): $(my_dexpreopt_zip)
 
@@ -477,3 +504,4 @@
   my_dexpreopt_zip :=
   my_dexpreopt_config_for_postprocessing :=
 endif # LOCAL_DEX_PREOPT
+endif # my_create_dexpreopt_config
\ No newline at end of file
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 8c25086..bb1aa1e 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -293,8 +293,11 @@
 #################################################################
 # Set up minimal BOOTCLASSPATH list of jars to build/execute
 # java code with dalvikvm/art.
-# Jars present in the ART apex. These should match exactly the list of
-# Java libraries in the ART apex build rule.
+# Jars present in the ART apex. These should match exactly the list of Java
+# libraries in art-bootclasspath-fragment. The APEX variant name
+# (com.android.art) is the same regardless which Soong module provides the ART
+# APEX. See the long comment in build/soong/java/dexprepopt_bootjars.go for
+# details.
 ART_APEX_JARS := \
     com.android.art:core-oj \
     com.android.art:core-libart \
diff --git a/core/envsetup.rbc b/core/envsetup.rbc
index 451623b..4cc98c8 100644
--- a/core/envsetup.rbc
+++ b/core/envsetup.rbc
@@ -38,7 +38,7 @@
     return all_versions[min_i:max_i + 1]
 
 # This function is a manual conversion of the version_defaults.mk
-def _versions_default(g, all_versions):
+def _versions_default(g, all_versions, v):
     """Handle various build version information.
 
     Guarantees that the following are defined:
@@ -59,17 +59,21 @@
         _build_id_init(g)
         g["INTERNAL_BUILD_ID_MAKEFILE"] = "build/make/core/build_id"
 
-    allowed_versions = _allowed_versions(all_versions, v_min, v_max, v_default)
-    g.setdefault("TARGET_PLATFORM_VERSION", v_default)
+    allowed_versions = _allowed_versions(all_versions, v.min_platform_version, v.max_platform_version, v.default_platform_version)
+    g.setdefault("TARGET_PLATFORM_VERSION", v.default_platform_version)
     if g["TARGET_PLATFORM_VERSION"] not in allowed_versions:
         fail("% is not valid, must be one of %s" % (g["TARGET_PLATFORM_VERSION"], allowed_versions))
 
-    g["DEFAULT_PLATFORM_VERSION"] = v_default
-    g["PLATFORM_VERSION_LAST_STABLE"] = 11
-    g.setdefault("PLATFORM_VERSION_CODENAME", g["TARGET_PLATFORM_VERSION"])
+    g["DEFAULT_PLATFORM_VERSION"] = v.default_platform_version
+    g["PLATFORM_VERSION_LAST_STABLE"] = v.platform_version_last_stable
+    target_platform_version = g["TARGET_PLATFORM_VERSION"]
+    if v.codenames[target_platform_version]:
+        g.setdefault("PLATFORM_VERSION_CODENAME", v.codenames[target_platform_version])
+    else:
+        g.setdefault("PLATFORM_VERSION_CODENAME", target_platform_version)
     # TODO(asmundak): set PLATFORM_VERSION_ALL_CODENAMES
 
-    g.setdefault("PLATFORM_SDK_VERSION", 30)
+    g.setdefault("PLATFORM_SDK_VERSION", v.platform_sdk_version)
     version_codename = g["PLATFORM_VERSION_CODENAME"]
     if version_codename == "REL":
         g.setdefault("PLATFORM_VERSION", g["PLATFORM_VERSION_LAST_STABLE"])
@@ -92,7 +96,8 @@
     #  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.
-    g.setdefault("PLATFORM_SECURITY_PATCH", "2021-03-05")
+
+    g.setdefault("PLATFORM_SECURITY_PATCH", v.platform_security_patch)
     dt = 'TZ="GMT" %s' % g["PLATFORM_SECURITY_PATCH"]
     g.setdefault("PLATFORM_SECURITY_PATCH_TIMESTAMP", rblf_shell("date -d '%s' +%%s" % dt))
 
@@ -116,16 +121,23 @@
     # in a warning being shown when any activity from the app is started.
     g.setdefault("PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION", 23)
 
-def init(g):
+    # This is the sdk extension version of this tree.
+    g["PLATFORM_SDK_EXTENSION_VERSION"] = v.platform_sdk_extension_version
+    # This is the sdk extension version that PLATFORM_SDK_VERSION ships with.
+    g["PLATFORM_BASE_SDK_EXTENSION_VERSION"] = v.platform_base_sdk_extension_version
+
+
+def init(g, v):
     """Initializes globals.
 
     The code is the Starlark counterpart of the contents of the
     envsetup.mk file.
     Args:
         g: globals dictionary
+        v: version info struct
     """
     all_versions = _all_versions()
-    _versions_default(g, all_versions)
+    _versions_default(g, all_versions, v)
     for v in all_versions:
         g["IS_AT_LEAST" + v] = True
         if v == g["TARGET_PLATFORM_VERSION"]:
@@ -188,9 +200,18 @@
     if g["HOST_BUILD_TYPE"] not in ["release", "debug"]:
         fail("HOST_BUILD_TYPE must be either release or debug, not '%s'" % g["HOST_BUILD_TYPE"])
 
+    g.update([
+	    ("TARGET_COPY_OUT_VENDOR", "||VENDOR-PATH-PH||"),
+    	("TARGET_COPY_OUT_PRODUCT", "||PRODUCT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_PRODUCT_SERVICES", "||PRODUCT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_SYSTEM_EXT", "||SYSTEM_EXT-PATH-PH||"),
+	    ("TARGET_COPY_OUT_ODM", "||ODM-PATH-PH||"),
+	    ("TARGET_COPY_OUT_VENDOR_DLKM", "||VENDOR_DLKM-PATH-PH||"),
+	    ("TARGET_COPY_OUT_ODM_DLKM", "||ODM_DLKM-PATH-PH||"),
+        ])
+
     # TODO(asmundak): there is more stuff in envsetup.mk lines 249-292, but
     # it does not seem to affect product configuration. Revisit this.
-
     g["ART_APEX_JARS"] = [
         "com.android.art:core-oj",
         "com.android.art:core-libart",
@@ -201,7 +222,3 @@
 
     if g.get("TARGET_BUILD_TYPE", "") != "debug":
         g["TARGET_BUILD_TYPE"] = "release"
-
-v_default = "SP1A"
-v_min = "SP1A"
-v_max = "SP1A"
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index 0f95202..07797c8 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -131,3 +131,8 @@
 ifeq ($(TURBINE_ENABLED),false)
 $(eval $(call copy-one-file,$(LOCAL_FULL_CLASSES_JACOCO_JAR),$(full_classes_header_jar)))
 endif
+
+#######################################
+# Capture deps added after base_rules.mk
+include $(BUILD_NOTICE_FILE)
+#######################################
diff --git a/core/java_renderscript.mk b/core/java_renderscript.mk
index 572d6e4..055ff14 100644
--- a/core/java_renderscript.mk
+++ b/core/java_renderscript.mk
@@ -107,7 +107,7 @@
 # Prevent these from showing up on the device
 # One exception is librsjni.so, which is needed for
 # both native path and compat path.
-rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni.so)/librsjni.so
+rs_jni_lib := $(call intermediates-dir-for,SHARED_LIBRARIES,librsjni)/librsjni.so
 LOCAL_JNI_SHARED_LIBRARIES += librsjni
 
 ifneq (,$(TARGET_BUILD_USE_PREBUILT_SDKS)$(FORCE_BUILD_RS_COMPAT))
diff --git a/core/main.mk b/core/main.mk
index c10a3cc..c57f7c9 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -115,15 +115,6 @@
 EMMA_INSTRUMENT := true
 endif
 
-ifeq (true,$(EMMA_INSTRUMENT))
-# Adding the jacoco library can cause the inclusion of
-# some typically banned classes
-# So if the user didn't specify SKIP_BOOT_JARS_CHECK, enable it here
-ifndef SKIP_BOOT_JARS_CHECK
-SKIP_BOOT_JARS_CHECK := true
-endif
-endif
-
 ifdef TARGET_ARCH_SUITE
   # TODO(b/175577370): Enable this error.
   # $(error TARGET_ARCH_SUITE is not supported in kati/make builds)
@@ -320,6 +311,13 @@
     ro.vendor.build.dont_use_vabc=true
 endif
 
+# Set the flag in vendor. So VTS would know if the new fingerprint format is in use when
+# the system images are replaced by GSI.
+ifeq ($(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT),true)
+ADDITIONAL_VENDOR_PROPERTIES += \
+    ro.vendor.build.fingerprint_has_digest=1
+endif
+
 ADDITIONAL_VENDOR_PROPERTIES += \
     ro.vendor.build.security_patch=$(VENDOR_SECURITY_PATCH) \
     ro.product.board=$(TARGET_BOOTLOADER_BOARD_NAME) \
@@ -350,7 +348,7 @@
 ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS)
 
 ifeq ($(AB_OTA_UPDATER),true)
-ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(AB_OTA_PARTITIONS))
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(strip $(AB_OTA_PARTITIONS)))
 endif
 
 # -----------------------------------------------------------------
@@ -361,7 +359,7 @@
 
 is_sdk_build :=
 
-ifneq ($(filter sdk win_sdk sdk_addon,$(MAKECMDGOALS)),)
+ifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),)
 is_sdk_build := true
 endif
 
@@ -536,13 +534,23 @@
 # Include all of the makefiles in the system
 #
 
-subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
+subdir_makefiles := $(SOONG_OUT_DIR)/installs-$(TARGET_PRODUCT).mk $(SOONG_ANDROID_MK)
+# Android.mk files are only used on Linux builds, Mac only supports Android.bp
+ifeq ($(HOST_OS),linux)
+  subdir_makefiles += $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
+endif
+subdir_makefiles += $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
 subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
 .KATI_READONLY := subdir_makefiles_total
 
 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
 
+# For an unbundled image, we can skip blueprint_tools because unbundled image
+# aims to remove a large number framework projects from the manifest, the
+# sources or dependencies for these tools may be missing from the tree.
+ifeq (,$(TARGET_BUILD_UNBUNDLED_IMAGE))
 droid_targets : blueprint_tools
+endif
 
 endif # dont_bother
 
@@ -1229,14 +1237,43 @@
 # Name resolution for LOCAL_REQUIRED_MODULES:
 #   See the select-bitness-of-required-modules definition.
 # $(1): product makefile
+
+# TODO(asmundak):
+# `product-installed-files` and `host-installed-files` macros below used to
+# call `get-product-var` directly to obtain per-file configuration variable
+# values (the value of variable FOO is fetched from PRODUCT.<product-makefile>.FOO).
+# Starlark-based configuration does not maintain per-file variable variable
+# values. To work around this problem, we utilize the fact that
+# `product-installed-files` and `host-installed-files` are called only in
+# two places:
+# 1. For the top-level product makefile (in this file). In this case
+#    $(call get-product-var <product>, FOO) is the same as $(FOO) as the
+#    product configuration has been run already. Therefore we define
+#    _product-var macro to pick the values directly from product config
+#    variables when using Starlark-based configuration.
+# 2. To check the path requirements (in artifact_path_requirements.mk).
+#    Starlark-based configuration does not perform this check at the moment.
+# In the longer run most of the logic of this file will be moved to the
+# Starlark.
+
+ifndef RBC_PRODUCT_CONFIG
+define _product-var
+  $(call get-product-var,$(1),$(2))
+endef
+else
+define _product-var
+  $(call $(2))
+endef
+endif
+
 define product-installed-files
   $(eval _pif_modules := \
-    $(call get-product-var,$(1),PRODUCT_PACKAGES) \
-    $(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \
-    $(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
-    $(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
-    $(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
-    $(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
+    $(call _product-var,$(1),PRODUCT_PACKAGES) \
+    $(if $(filter eng,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_ENG)) \
+    $(if $(filter debug,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \
+    $(if $(filter tests,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_TESTS)) \
+    $(if $(filter asan,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \
+    $(if $(filter java_coverage,$(tags_to_install)),$(call _product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \
     $(call auto-included-modules) \
   ) \
   $(eval ### Filter out the overridden packages and executables before doing expansion) \
@@ -1247,13 +1284,13 @@
   $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \
   $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \
   $(call resolve-product-relative-paths,\
-    $(foreach cf,$(call get-product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
+    $(foreach cf,$(call _product-var,$(1),PRODUCT_COPY_FILES),$(call word-colon,2,$(cf))))
 endef
 
 # Similar to product-installed-files above, but handles PRODUCT_HOST_PACKAGES instead
 # This does support the :32 / :64 syntax, but does not support module overrides.
 define host-installed-files
-  $(eval _hif_modules := $(call get-product-var,$(1),PRODUCT_HOST_PACKAGES)) \
+  $(eval _hif_modules := $(call _product-var,$(1),PRODUCT_HOST_PACKAGES)) \
   $(eval ### Split host vs host cross modules) \
   $(eval _hcif_modules := $(filter host_cross_%,$(_hif_modules))) \
   $(eval _hif_modules := $(filter-out host_cross_%,$(_hif_modules))) \
@@ -1278,7 +1315,11 @@
 )
 endef
 
-ifdef FULL_BUILD
+ifeq ($(HOST_OS),darwin)
+  # Target builds are not supported on Mac
+  product_target_FILES :=
+  product_host_FILES := $(call host-installed-files,$(INTERNAL_PRODUCT))
+else ifdef FULL_BUILD
   ifneq (true,$(ALLOW_MISSING_DEPENDENCIES))
     # Check to ensure that all modules in PRODUCT_PACKAGES exist (opt in per product)
     ifeq (true,$(PRODUCT_ENFORCE_PACKAGES_EXIST))
@@ -1338,7 +1379,7 @@
 
   # Verify the artifact path requirements made by included products.
   is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)
-  ifneq (true,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))
+  ifeq (,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS),$(RBC_PRODUCT_CONFIG)))
     include $(BUILD_SYSTEM)/artifact_path_requirements.mk
   endif
 else
@@ -1434,7 +1475,9 @@
 # contains everything that's built during the current make, but it also further
 # extends ALL_DEFAULT_INSTALLED_MODULES.
 ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
-include $(BUILD_SYSTEM)/Makefile
+ifeq ($(HOST_OS),linux)
+  include $(BUILD_SYSTEM)/Makefile
+endif
 modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
 ALL_DEFAULT_INSTALLED_MODULES :=
 
@@ -1533,6 +1576,9 @@
 .PHONY: vendorramdisk_debug
 vendorramdisk_debug: $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET)
 
+.PHONY: vendorramdisk_test_harness
+vendorramdisk_test_harness: $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET)
+
 .PHONY: productimage
 productimage: $(INSTALLED_PRODUCTIMAGE_TARGET)
 
@@ -1557,6 +1603,10 @@
 .PHONY: bootimage
 bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
 
+ifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST))
+$(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET))
+endif
+
 .PHONY: bootimage_debug
 bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
 
@@ -1592,6 +1642,7 @@
     $(INSTALLED_VENDORIMAGE_TARGET) \
     $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \
     $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \
+    $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \
     $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \
     $(INSTALLED_VENDOR_RAMDISK_TARGET) \
     $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \
@@ -1631,8 +1682,7 @@
     $(INSTALLED_FILES_JSON_ROOT) \
     $(INSTALLED_FILES_FILE_RECOVERY) \
     $(INSTALLED_FILES_JSON_RECOVERY) \
-    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
-    soong_docs
+    $(INSTALLED_ANDROID_INFO_TXT_TARGET)
 
 # The droidcore target depends on the droidcore-unbundled subset and any other
 # targets for a non-unbundled (full source) full system build.
@@ -1648,7 +1698,11 @@
 endif
 
 .PHONY: apps_only
-ifneq ($(TARGET_BUILD_APPS),)
+ifeq ($(HOST_OS),darwin)
+  # Mac only supports building host modules
+  droid_targets: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) dist_files
+
+else ifneq ($(TARGET_BUILD_APPS),)
   # If this build is just for apps, only build apps and not the full system by default.
 
   unbundled_build_modules :=
@@ -1731,7 +1785,6 @@
   $(call dist-for-goals, droidcore, \
     $(BUILT_OTATOOLS_PACKAGE) \
     $(APPCOMPAT_ZIP) \
-    $(DEXPREOPT_CONFIG_ZIP) \
     $(DEXPREOPT_TOOLS_ZIP) \
   )
 
@@ -1779,6 +1832,7 @@
     $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
     $(INSTALLED_MISC_INFO_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
+    $(DEXPREOPT_CONFIG_ZIP) \
   )
 
   # Put a copy of the radio/bootloader files in the dist dir.
@@ -1812,6 +1866,7 @@
       $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
       $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \
       $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET) \
+      $(INSTALLED_VENDOR_TEST_HARNESS_RAMDISK_TARGET) \
       $(INSTALLED_VENDOR_TEST_HARNESS_BOOTIMAGE_TARGET) \
       $(INSTALLED_VENDOR_RAMDISK_TARGET) \
       $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) \
@@ -1847,6 +1902,8 @@
   ifdef CLANG_COVERAGE
     $(foreach f,$(SOONG_NDK_API_XML), \
         $(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f))))
+    $(foreach f,$(SOONG_CC_API_XML), \
+        $(call dist-for-goals,droidcore,$(f):cc_apis/$(notdir $(f))))
   endif
 
   # For full system build (whether unbundled or not), we configure
@@ -1872,16 +1929,18 @@
 .PHONY: docs
 docs: $(ALL_DOCS)
 
-.PHONY: sdk win_sdk winsdk-tools sdk_addon
+.PHONY: sdk sdk_addon
+ifeq ($(HOST_OS),linux)
 ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
 sdk: $(ALL_SDK_TARGETS)
-$(call dist-for-goals,sdk win_sdk, \
+$(call dist-for-goals,sdk, \
     $(ALL_SDK_TARGETS) \
     $(SYMBOLS_ZIP) \
     $(COVERAGE_ZIP) \
     $(APPCOMPAT_ZIP) \
     $(INSTALLED_BUILD_PROP_TARGET) \
 )
+endif
 
 # umbrella targets to assit engineers in verifying builds
 .PHONY: java native target host java-host java-target native-host native-target \
diff --git a/core/ninja_config.mk b/core/ninja_config.mk
index 2e1bd69..2157c9e 100644
--- a/core/ninja_config.mk
+++ b/core/ninja_config.mk
@@ -38,15 +38,19 @@
 	test-art% \
 	user \
 	userdataimage \
-	userdebug \
-	win_sdk \
-	winsdk-tools
+	userdebug
 
 include $(wildcard vendor/*/build/ninja_config.mk)
 
 # Any Android goals that need to be built.
 ANDROID_GOALS := $(filter-out $(KATI_OUTPUT_PATTERNS),\
     $(sort $(ORIGINAL_MAKECMDGOALS) $(MAKECMDGOALS)))
+# Temporary compatibility support until the build server configs are updated
+ANDROID_GOALS := $(patsubst win_sdk,sdk,$(ANDROID_GOALS))
+ifneq ($(HOST_OS),linux)
+  ANDROID_GOALS := $(filter-out sdk,$(ANDROID_GOALS))
+  ANDROID_GOALS := $(patsubst sdk_repo,sdk-repo-build-tools sdk-repo-platform-tools,$(ANDROID_GOALS))
+endif
 # Goals we need to pass to Ninja.
 NINJA_GOALS := $(filter-out $(NINJA_EXCLUDE_GOALS), $(ANDROID_GOALS))
 ifndef NINJA_GOALS
diff --git a/core/product-graph.mk b/core/product-graph.mk
index 968d01b..de4e581 100644
--- a/core/product-graph.mk
+++ b/core/product-graph.mk
@@ -81,6 +81,7 @@
 $(products_graph): PRIVATE_PRODUCTS_FILTER := $(products_list)
 
 $(products_graph): $(this_makefile)
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH))
 	@echo Product graph DOT: $@ for $(PRIVATE_PRODUCTS_FILTER)
 	$(hide) echo 'digraph {' > $@.in
 	$(hide) echo 'graph [ ratio=.5 ];' >> $@.in
@@ -89,6 +90,10 @@
 	$(foreach p,$(PRIVATE_PRODUCTS),$(call emit-product-node-props,$(p),$@.in))
 	$(hide) echo '}' >> $@.in
 	$(hide) build/make/tools/filter-product-graph.py $(PRIVATE_PRODUCTS_FILTER) < $@.in > $@
+else
+	@echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+	false
+endif
 
 # Evaluates to the name of the product file
 # $(1) product file
@@ -143,6 +148,7 @@
 	$(hide) cat $$< | build/make/tools/product_debug.py > $$@
 endef
 
+ifeq (,$(RBC_PRODUCT_CONFIG)$(RBC_NO_PRODUCT_GRAPH))
 product_debug_files:=
 $(foreach p,$(all_products), \
 			$(eval $(call transform-product-debug, $(p))) \
@@ -154,3 +160,8 @@
 	@echo Product graph .dot file: $(products_graph)
 	@echo Command to convert to pdf: dot -Tpdf -Nshape=box -o $(OUT_DIR)/products.pdf $(products_graph)
 	@echo Command to convert to svg: dot -Tsvg -Nshape=box -o $(OUT_DIR)/products.svg $(products_graph)
+else
+.PHONY: product-graph
+	@echo RBC_PRODUCT_CONFIG and RBC_NO_PRODUCT_GRAPH should be unset to generate product graph
+	false
+endif
\ No newline at end of file
diff --git a/core/product.mk b/core/product.mk
index 015fe44..23fb939 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -213,12 +213,18 @@
 # The list of product-specific kernel header dirs
 _product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS
 
-# A list of module names of BOOTCLASSPATH (jar files)
+# A list of module names in BOOTCLASSPATH (jar files). Each module may be
+# prefixed with "<apex>:", which identifies the APEX that provides it. APEXes
+# are identified by their "variant" names, i.e. their `apex_name` values in
+# Soong, which default to the `name` values. The prefix can also be "platform:"
+# or "system_ext:", and defaults to "platform:" if left out. See the long
+# comment in build/soong/java/dexprepopt_bootjars.go for details.
 _product_list_vars += PRODUCT_BOOT_JARS
 
-# A list of extra BOOTCLASSPATH jars (to be appended after common jars).
-# Products that include device-specific makefiles before AOSP makefiles should use this
-# instead of PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.
+# A list of extra BOOTCLASSPATH jars (to be appended after common jars),
+# following the same format as PRODUCT_BOOT_JARS. Products that include
+# device-specific makefiles before AOSP makefiles should use this instead of
+# PRODUCT_BOOT_JARS, so that device-specific jars go after common jars.
 _product_list_vars += PRODUCT_BOOT_JARS_EXTRA
 
 _product_single_value_vars += PRODUCT_SUPPORTS_BOOT_SIGNER
@@ -228,9 +234,11 @@
 _product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
 _product_list_vars += PRODUCT_SYSTEM_SERVER_JARS
 # List of system_server jars delivered via apex. Format = <apex name>:<jar name>.
-_product_list_vars += PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS
+_product_list_vars += PRODUCT_APEX_SYSTEM_SERVER_JARS
 # If true, then suboptimal order of system server jars does not cause an error.
 _product_single_value_vars += PRODUCT_BROKEN_SUBOPTIMAL_ORDER_OF_SYSTEM_SERVER_JARS
+# If true, then system server jars defined in Android.mk are supported.
+_product_single_value_vars += PRODUCT_BROKEN_DEPRECATED_MK_SYSTEM_SERVER_JARS
 
 # Additional system server jars to be appended at the end of the common list.
 # This is necessary to avoid jars reordering due to makefile inheritance order.
@@ -268,6 +276,7 @@
 
 # Boot image options.
 _product_single_value_vars += \
+    PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST \
     PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \
     PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION \
     PRODUCT_USES_DEFAULT_ART_CONFIG \
@@ -366,11 +375,6 @@
 _product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
 _product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
 
-# A list of <overridden-apex>:<override-apex> pairs that specifies APEX module
-# overrides to be applied to the APEX names in the boot jar variables
-# (PRODUCT_BOOT_JARS, PRODUCT_UPDATABLE_BOOT_JARS etc).
-_product_list_vars += PRODUCT_BOOT_JAR_MODULE_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
@@ -385,12 +389,16 @@
 _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_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE
 
-# List of boot jars delivered via apex
-_product_list_vars += PRODUCT_UPDATABLE_BOOT_JARS
+# List of boot jars delivered via updatable APEXes, following the same format as
+# PRODUCT_BOOT_JARS.
+_product_list_vars += PRODUCT_APEX_BOOT_JARS
 
 # If set, device uses virtual A/B.
 _product_single_value_vars += PRODUCT_VIRTUAL_AB_OTA
@@ -427,6 +435,11 @@
 
 _product_single_value_vars += PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES
 
+# 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.
+_product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
+
 .KATI_READONLY := _product_single_value_vars _product_list_vars
 _product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
 
diff --git a/core/product_config.mk b/core/product_config.mk
index 5c85fb8..33b15d3 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -77,6 +77,46 @@
 $(sort $(shell find $(2) -name "$(1)" -type f | $(SED_EXTENDED) "s:($(2)/?(.*)):\\1\\:$(3)/\\2:" | sed "s://:/:g"))
 endef
 
+#
+# Convert file file to the PRODUCT_COPY_FILES/PRODUCT_SDK_ADDON_COPY_FILES
+# format: for each file F return $(F):$(PREFIX)/$(notdir $(F))
+# $(1): files list
+# $(2): prefix
+
+define copy-files
+$(foreach f,$(1),$(f):$(2)/$(notdir $(f)))
+endef
+
+#
+# Convert the list of file names to the list of PRODUCT_COPY_FILES items
+# $(1): from pattern
+# $(2): to pattern
+# $(3): file names
+# E.g., calling product-copy-files-by-pattern with
+#   (from/%, to/%, a b)
+# returns
+#   from/a:to/a from/b:to/b
+define product-copy-files-by-pattern
+$(join $(patsubst %,$(1),$(3)),$(patsubst %,:$(2),$(3)))
+endef
+
+# Return empty unless the board matches
+define is-board-platform2
+$(filter $(1), $(TARGET_BOARD_PLATFORM))
+endef
+
+# Return empty unless the board is in the list
+define is-board-platform-in-list2
+$(filter $(1),$(TARGET_BOARD_PLATFORM))
+endef
+
+# Return empty unless the board is QCOM
+define is-vendor-board-qcom
+$(if $(strip $(TARGET_BOARD_PLATFORM) $(QCOM_BOARD_PLATFORMS)),\
+  $(filter $(TARGET_BOARD_PLATFORM),$(QCOM_BOARD_PLATFORMS)),\
+  $(error both TARGET_BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) and QCOM_BOARD_PLATFORMS=$(QCOM_BOARD_PLATFORMS)))
+endef
+
 # ---------------------------------------------------------------
 # Check for obsolete PRODUCT- and APP- goals
 ifeq ($(CALLED_FROM_SETUP),true)
@@ -162,11 +202,24 @@
 ifneq (1,$(words $(current_product_makefile)))
 $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile))
 endif
+
+ifndef RBC_PRODUCT_CONFIG
 $(call import-products, $(current_product_makefile))
+else
+  rc := $(shell build/soong/scripts/rbc-run $(current_product_makefile) \
+      >$(OUT_DIR)/rbctemp.mk || echo $$?)
+  ifneq (,$(rc))
+    $(error product configuration converter failed: $(rc))
+  endif
+  include $(OUT_DIR)/rbctemp.mk
+  PRODUCTS += $(current_product_makefile)
+endif
 endif  # Import all or just the current product makefile
 
+ifndef RBC_PRODUCT_CONFIG
 # Quick check
 $(check-all-products)
+endif
 
 ifeq ($(SKIP_ARTIFACT_PATH_REQUIREMENT_PRODUCTS_CHECK),)
 # Import all the products that have made artifact path requirements, so that we can verify
@@ -186,6 +239,7 @@
 $(dump-products)
 endif
 
+ifndef RBC_PRODUCT_CONFIG
 # Convert a short name like "sooner" into the path to the product
 # file defining that product.
 #
@@ -198,6 +252,9 @@
 ############################################################################
 # Strip and assign the PRODUCT_ variables.
 $(call strip-product-vars)
+else
+INTERNAL_PRODUCT := $(current_product_makefile)
+endif
 
 current_product_makefile :=
 all_product_makefiles :=
@@ -249,18 +306,14 @@
 
 PRODUCT_BOOT_JARS := $(call qualify-platform-jars,$(PRODUCT_BOOT_JARS))
 
-# Replaces references to overridden boot jar modules in a boot jars variable.
-# $(1): Name of a boot jars variable with <apex>:<jar> pairs.
-define replace-boot-jar-module-overrides
-  $(foreach pair,$(PRODUCT_BOOT_JAR_MODULE_OVERRIDES),\
-    $(eval _rbjmo_from := $(call word-colon,1,$(pair)))\
-    $(eval _rbjmo_to := $(call word-colon,2,$(pair)))\
-    $(eval $(1) := $(patsubst $(_rbjmo_from):%,$(_rbjmo_to):%,$($(1)))))
-endef
-
-$(call replace-boot-jar-module-overrides,PRODUCT_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,PRODUCT_UPDATABLE_BOOT_JARS)
-$(call replace-boot-jar-module-overrides,ART_APEX_JARS)
+# b/191127295: force core-icu4j onto boot image. It comes from a non-updatable APEX jar, but has
+# historically been part of the boot image; even though APEX jars are not meant to be part of the
+# boot image.
+# TODO(b/191686720): remove PRODUCT_APEX_BOOT_JARS to avoid a special handling of core-icu4j
+# in make rules.
+PRODUCT_APEX_BOOT_JARS := $(filter-out com.android.i18n:core-icu4j,$(PRODUCT_APEX_BOOT_JARS))
+# All APEX jars come after /system and /system_ext jars, so adding core-icu4j at the end of the list
+PRODUCT_BOOT_JARS += com.android.i18n:core-icu4j
 
 # The extra system server jars must be appended at the end after common system server jars.
 PRODUCT_SYSTEM_SERVER_JARS += $(PRODUCT_SYSTEM_SERVER_JARS_EXTRA)
@@ -303,10 +356,10 @@
   endif
 endif
 
-$(foreach pair,$(PRODUCT_UPDATABLE_BOOT_JARS), \
+$(foreach pair,$(PRODUCT_APEX_BOOT_JARS), \
   $(eval jar := $(call word-colon,2,$(pair))) \
   $(if $(findstring $(jar), $(PRODUCT_BOOT_JARS)), \
-    $(error A jar in PRODUCT_UPDATABLE_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
+    $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
 
 ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)
 ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)
@@ -350,6 +403,12 @@
   $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))
 endif
 
+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)
+  endif
+endif
+
 ifndef PRODUCT_USE_DYNAMIC_PARTITIONS
   PRODUCT_USE_DYNAMIC_PARTITIONS := $(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)
 endif
@@ -475,6 +534,7 @@
 
 # Copy and check the value of each PRODUCT_BUILD_*_IMAGE variable
 $(foreach image, \
+    PVMFW \
     SYSTEM \
     SYSTEM_OTHER \
     VENDOR \
diff --git a/core/product_config.rbc b/core/product_config.rbc
index 111e759..9876d56 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -16,7 +16,8 @@
 
 """Runtime functions."""
 
-def _global_init():
+_soong_config_namespaces_key = "$SOONG_CONFIG_NAMESPACES"
+def _global_init(version_info):
     """Returns dict created from the runtime environment."""
     globals = dict()
 
@@ -29,7 +30,8 @@
         globals[k] = getattr(rblf_cli, k)
 
     globals.setdefault("PRODUCT_SOONG_NAMESPACES", [])
-    _envsetup_init(globals)
+    globals.setdefault(_soong_config_namespaces_key, {})
+    _envsetup_init(globals, version_info)
 
     # Variables that should be defined.
     mandatory_vars = [
@@ -44,10 +46,8 @@
     for bv in mandatory_vars:
         if not bv in globals:
             fail(bv, " is not defined")
-
     return globals
 
-_globals_base = _global_init()
 
 def __print_attr(attr, value):
     if not value:
@@ -62,18 +62,32 @@
     elif _options.format == "pretty":
         print(attr, "=", repr(value))
     elif _options.format == "make":
-        print(attr, ":=", value)
+        # Trim all spacing to a single space
+        print(attr, ":=", _mkstrip(value))
     else:
         fail("bad output format", _options.format)
 
-def _printvars(globals, cfg):
-    """Prints known configuration variables."""
+def _printvars(state):
+    """Prints configuration and global variables."""
+    (globals, cfg, globals_base) = state
     for attr, val in sorted(cfg.items()):
         __print_attr(attr, val)
     if _options.print_globals:
         print()
         for attr, val in sorted(globals.items()):
-            if attr not in _globals_base:
+            if attr == _soong_config_namespaces_key:
+                __print_attr("SOONG_CONFIG_NAMESPACES", val.keys())
+                for nsname, nsvars in sorted(val.items()):
+                    # Define SOONG_CONFIG_<ns> for Make, othewise
+                    # it cannot be added to .KATI_READONLY list
+                    if _options.format == "make":
+                        print("SOONG_CONFIG_" + nsname, ":=", " ".join(nsvars.keys()))
+                    for var, val in sorted(nsvars.items()):
+                        if val:
+                            __print_attr("SOONG_CONFIG_%s_%s" % (nsname, var), val)
+                        else:
+                            print("SOONG_CONFIG_%s_%s :=" % (nsname, var))
+            elif attr not in globals_base or globals_base[attr] != val:
                 __print_attr(attr, val)
 
 def __printvars_rearrange_list(value_list):
@@ -81,7 +95,7 @@
     seen = {item: 0 for item in value_list}
     return sorted(seen.keys()) if _options.rearrange == "sort" else seen.keys()
 
-def _product_configuration(top_pcm_name, top_pcm):
+def _product_configuration(top_pcm_name, top_pcm, version_info):
     """Creates configuration."""
 
     # Product configuration is created by traversing product's inheritance
@@ -95,7 +109,8 @@
     # PCM means "Product Configuration Module", i.e., a Starlark file
     # whose body consists of a single init function.
 
-    globals = dict(**_globals_base)
+    globals_base = _global_init(version_info)
+    globals = dict(**globals_base)
 
     config_postfix = []  # Configs in postfix order
 
@@ -186,7 +201,7 @@
         _percolate_inherited(configs, pcm_name, cfg, children_names)
         configs[pcm_name] = pcm, cfg, children_names, True
 
-    return globals, configs[top_pcm_name][1]
+    return (globals, configs[top_pcm_name][1], globals_base)
 
 def _substitute_inherited(configs, pcm_name, cfg):
     """Substitutes inherited values in all the attributes.
@@ -267,6 +282,36 @@
     """Returns configuration item for the inherited module."""
     return (pcm_name,)
 
+def _soong_config_namespace(g, nsname):
+    """Adds given namespace if it does not exist."""
+
+    if g[_soong_config_namespaces_key].get(nsname):
+        return
+    # A value cannot be updated, so we need to create a new dictionary
+    old = g[_soong_config_namespaces_key]
+    g[_soong_config_namespaces_key] = dict([(k,v) for k,v in old.items()] + [(nsname, {})])
+
+def _soong_config_set(g, nsname, var, value):
+    """Assigns the value to the variable in the namespace."""
+    _soong_config_namespace(g, nsname)
+    g[_soong_config_namespaces_key][nsname][var]=value
+
+def _soong_config_append(g, nsname, var, value):
+    """Appends to the value of the variable in the namespace."""
+    _soong_config_namespace(g, nsname)
+    ns = g[_soong_config_namespaces_key][nsname]
+    oldv = ns.get(var)
+    if oldv == None:
+        ns[var] = value
+    else:
+        ns[var] += " " + value
+
+
+def _abspath(path):
+    """Provided for compatibility, to be removed later."""
+    return path
+
+
 def _addprefix(prefix, string_or_list):
     """Adds prefix and returns a list.
 
@@ -347,6 +392,27 @@
         if type(val) == "list":
             val.append(_indirect(pcm_name))
 
+def __base(path):
+    """Returns basename."""
+    return path.rsplit("/",1)[-1]
+
+def _board_platform_in(g, string_or_list):
+    """Returns true if board is in the list."""
+    board = g.get("TARGET_BOARD_PLATFORM","")
+    if not board:
+        return False
+    return board in __words(string_or_list)
+
+
+def _board_platform_is(g, s):
+    """True if board is the same as argument."""
+    return g.get("TARGET_BOARD_PLATFORM","") == s
+
+
+def _copy_files(l, outdir):
+    """Generate <item>:<outdir>/item for each item."""
+    return ["%s:%s/%s" % (path, outdir, __base(path)) for path in __words(l)]
+
 def _copy_if_exists(path_pair):
     """If from file exists, returns [from:to] pair."""
     value = path_pair.split(":", 2)
@@ -366,7 +432,8 @@
 
 def _find_and_copy(pattern, from_dir, to_dir):
     """Return a copy list for the files matching the pattern."""
-    return ["%s/%s:%s/%s" % (from_dir, f, to_dir, f) for f in rblf_wildcard(pattern, from_dir)]
+    return sorted(["%s/%s:%s/%s" % (
+        from_dir, f, to_dir, f) for f in rblf_find_files(from_dir, pattern, only_files=1)])
 
 def _filter_out(pattern, text):
     """Return all the words from `text' that do not match any word in `pattern'.
@@ -403,7 +470,7 @@
     """Returns regular expression equivalent to Make pattern."""
 
     # TODO(asmundak): this will mishandle '\%'
-    return "^(" + "|".join([w.replace("%", ".*", 1) for w in words]) + ")"
+    return "^(" + "|".join([w.replace("%", ".*", 1) for w in words if w]) + ")$"
 
 def _regex_match(regex, w):
     return rblf_regex(regex, w)
@@ -426,11 +493,109 @@
 
 def _mkwarning(file, message = ""):
     """Prints warning."""
-    print("%s: warning: %s" % (file, message))
+    rblf_log(file, "warning", message, sep = ':')
 
 def _mkinfo(file, message = ""):
     """Prints info."""
-    print(message)
+    rblf_log(message)
+
+
+def __mkparse_pattern(pattern):
+    """Parses Make's patsubst pattern."""
+    in_escape = False
+    res = []
+    acc = ""
+    for c in pattern.elems():
+        if in_escape:
+            in_escape = False
+            acc += c
+        elif c == '\\':
+            in_escape = True
+        elif c == '%' and not res:
+            res.append(acc)
+            acc = ''
+        else:
+            acc += c
+    if in_escape:
+        acc += '\\'
+    res.append(acc)
+    return res
+
+
+def __mkpatsubst_word(parsed_pattern,parsed_subst, word):
+    (before, after) = parsed_pattern
+    if not word.startswith(before):
+        return word
+    if not word.endswith(after):
+        return word
+    if len(parsed_subst) < 2:
+        return parsed_subst[0]
+    return parsed_subst[0] + word[len(before):len(word) - len(after)] + parsed_subst[1]
+
+
+def _mkpatsubst(pattern, replacement, s):
+    """Emulates Make's patsubst.
+
+    Tokenizes `s` (unless it is already a list), and then performs a simple
+    wildcard substitution (in other words, `foo%bar` pattern is equivalent to
+    the regular expression `^foo(.*)bar$, and the first `%` in replacement is
+    $1 in regex terms).
+    """
+    parsed_pattern = __mkparse_pattern(pattern)
+    words = s if type(s) == "list" else _mkstrip(s).split(" ")
+    if len(parsed_pattern) == 1:
+        out_words = [ replacement if x == pattern else x for x in words]
+    else:
+        parsed_replacement = __mkparse_pattern(replacement)
+        out_words = [__mkpatsubst_word(parsed_pattern, parsed_replacement, x) for x in words]
+    return out_words if type(s) == "list" else " ".join(out_words)
+
+
+def _mkstrip(s):
+    """Emulates Make's strip.
+
+    That is, removes string's leading and trailing whitespace characters and
+    replaces any sequence of whitespace characters with with a single space.
+    """
+    if type(s) != "string":
+        return s
+    result = ""
+    was_space = False
+    for ch in s.strip().elems():
+        is_space = ch.isspace()
+        if not is_space:
+            if was_space:
+                result += " "
+            result += ch
+        was_space = is_space
+    return result
+
+def _mksubst(old, new, s):
+    """Emulates Make's subst.
+
+    Replaces each occurence of 'old' with 'new'.
+    If 's' is a list, applies substitution to each item.
+    """
+    if type(s) == "list":
+        return [e.replace(old, new) for e in s]
+    return s.replace(old, new)
+
+
+def _product_copy_files_by_pattern(src, dest, s):
+    """Creates a copy list.
+
+    For each item in a given list, create <from>:<to> pair, where <from> and
+    <to> are the results of applying Make-style patsubst of <src> and <dest>
+    respectively. E.g. the result of calling this function with
+    ("foo/%", "bar/%", ["a", "b"])  will be
+    ["foo/a:bar/a", "foo/b:bar/b"].
+    """
+    parsed_src = __mkparse_pattern(src)
+    parsed_dest = __mkparse_pattern(dest)
+    parsed_percent = ["", ""]
+    words = s if type(s) == "list" else _mkstrip(s).split(" ")
+    return [ __mkpatsubst_word(parsed_percent, parsed_src, x) + ":" + __mkpatsubst_word(parsed_percent, parsed_dest, x) for x in words]
+
 
 def __get_options():
     """Returns struct containing runtime global settings."""
@@ -462,8 +627,15 @@
 # Settings used during debugging.
 _options = __get_options()
 rblf = struct(
+    soong_config_namespace = _soong_config_namespace,
+    soong_config_append = _soong_config_append,
+    soong_config_set = _soong_config_set,
+    abspath = _abspath,
     addprefix = _addprefix,
     addsuffix = _addsuffix,
+    board_platform_in = _board_platform_in,
+    board_platform_is = _board_platform_is,
+    copy_files = _copy_files,
     copy_if_exists = _copy_if_exists,
     cfg = __h_cfg,
     enforce_product_packages_exist = _enforce_product_packages_exist,
@@ -478,9 +650,13 @@
     indirect = _indirect,
     mkinfo = _mkinfo,
     mkerror = _mkerror,
+    mkpatsubst = _mkpatsubst,
     mkwarning = _mkwarning,
+    mkstrip = _mkstrip,
+    mksubst = _mksubst,
     printvars = _printvars,
     product_configuration = _product_configuration,
+    product_copy_files_by_pattern = _product_copy_files_by_pattern,
     require_artifacts_in_path = _require_artifacts_in_path,
     require_artifacts_in_path_relaxed = _require_artifacts_in_path_relaxed,
     setdefault = _setdefault,
diff --git a/core/rbe.mk b/core/rbe.mk
index 19c0e42..370d4bd 100644
--- a/core/rbe.mk
+++ b/core/rbe.mk
@@ -22,6 +22,18 @@
     rbe_dir := prebuilts/remoteexecution-client/live/
   endif
 
+  ifdef RBE_CXX_POOL
+    cxx_pool := $(RBE_CXX_POOL)
+  else
+    cxx_pool := default
+  endif
+
+  ifdef RBE_JAVA_POOL
+    java_pool := $(RBE_JAVA_POOL)
+  else
+    java_pool := java16
+  endif
+
   ifdef RBE_CXX_EXEC_STRATEGY
     cxx_rbe_exec_strategy := $(RBE_CXX_EXEC_STRATEGY)
   else
@@ -59,8 +71,8 @@
   endif
 
   platform := container-image=docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62
-  cxx_platform := $(platform),Pool=default
-  java_r8_d8_platform := $(platform),Pool=java16
+  cxx_platform := $(platform),Pool=$(cxx_pool)
+  java_r8_d8_platform := $(platform),Pool=$(java_pool)
 
   RBE_WRAPPER := $(rbe_dir)/rewrapper
   RBE_CXX := --labels=type=compile,lang=cpp,compiler=clang --env_var_allowlist=PWD --exec_strategy=$(cxx_rbe_exec_strategy) --platform=$(cxx_platform) --compare=$(cxx_compare)
diff --git a/core/rust_device_test_config_template.xml b/core/rust_device_test_config_template.xml
index 9429d38..bfd2f47 100644
--- a/core/rust_device_test_config_template.xml
+++ b/core/rust_device_test_config_template.xml
@@ -15,6 +15,9 @@
 -->
 <!-- This test config file is auto-generated. -->
 <configuration description="Config to run {MODULE} device tests.">
+
+    {EXTRA_CONFIGS}
+
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
diff --git a/core/soong_android_app_set.mk b/core/soong_android_app_set.mk
index ef9eace..f994165 100644
--- a/core/soong_android_app_set.mk
+++ b/core/soong_android_app_set.mk
@@ -9,6 +9,11 @@
 LOCAL_BUILT_MODULE_STEM := $(LOCAL_APK_SET_INSTALL_FILE)
 LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_APK_SET_INSTALL_FILE)
 
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
diff --git a/core/soong_app_prebuilt.mk b/core/soong_app_prebuilt.mk
index 82fb413..ee06432 100644
--- a/core/soong_app_prebuilt.mk
+++ b/core/soong_app_prebuilt.mk
@@ -28,6 +28,17 @@
 full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar
 full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar
 
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_CLASSES_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR)
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
@@ -254,3 +265,8 @@
 endif
 
 SOONG_ALREADY_CONV += $(LOCAL_MODULE)
+
+#######################################
+# Capture deps added after base_rules.mk
+include $(BUILD_NOTICE_FILE)
+#######################################
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_prebuilt.mk
index 4d7b614..49345e2 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_prebuilt.mk
@@ -45,6 +45,11 @@
   endif
 endif
 
+
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
diff --git a/core/soong_config.mk b/core/soong_config.mk
index ec67560..4999464 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -27,6 +27,7 @@
 $(call add_json_val,  Platform_sdk_version,              $(PLATFORM_SDK_VERSION))
 $(call add_json_str,  Platform_sdk_codename,             $(PLATFORM_VERSION_CODENAME))
 $(call add_json_bool, Platform_sdk_final,                $(filter REL,$(PLATFORM_VERSION_CODENAME)))
+$(call add_json_val,  Platform_sdk_extension_version,    $(PLATFORM_SDK_EXTENSION_VERSION))
 $(call add_json_csv,  Platform_version_active_codenames, $(PLATFORM_VERSION_ALL_CODENAMES))
 $(call add_json_str,  Platform_security_patch,           $(PLATFORM_SECURITY_PATCH))
 $(call add_json_str,  Platform_preview_sdk_version,      $(PLATFORM_PREVIEW_SDK_VERSION))
@@ -37,8 +38,8 @@
 $(call add_json_bool, Allow_missing_dependencies,        $(filter true,$(ALLOW_MISSING_DEPENDENCIES)))
 $(call add_json_bool, Unbundled_build,                   $(TARGET_BUILD_UNBUNDLED))
 $(call add_json_bool, Unbundled_build_apps,              $(TARGET_BUILD_APPS))
+$(call add_json_bool, Unbundled_build_image,             $(TARGET_BUILD_UNBUNDLED_IMAGE))
 $(call add_json_bool, Always_use_prebuilt_sdks,          $(TARGET_BUILD_USE_PREBUILT_SDKS))
-$(call add_json_bool, Skip_boot_jars_check,              $(SKIP_BOOT_JARS_CHECK))
 
 $(call add_json_bool, Debuggable,                        $(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 $(call add_json_bool, Eng,                               $(filter eng,$(TARGET_BUILD_VARIANT)))
@@ -72,6 +73,7 @@
 $(call add_json_str,  HostArch,                          $(HOST_ARCH))
 $(call add_json_str,  HostSecondaryArch,                 $(HOST_2ND_ARCH))
 $(call add_json_bool, HostStaticBinaries,                $(BUILD_HOST_static))
+$(call add_json_bool, HostMusl,                          $(USE_HOST_MUSL))
 
 $(call add_json_str,  CrossHost,                         $(HOST_CROSS_OS))
 $(call add_json_str,  CrossHostArch,                     $(HOST_CROSS_ARCH))
@@ -141,7 +143,7 @@
 $(call add_json_list, ModulesLoadedByPrivilegedModules,  $(PRODUCT_LOADED_BY_PRIVILEGED_MODULES))
 
 $(call add_json_list, BootJars,                          $(PRODUCT_BOOT_JARS))
-$(call add_json_list, UpdatableBootJars,                 $(PRODUCT_UPDATABLE_BOOT_JARS))
+$(call add_json_list, ApexBootJars,                      $(PRODUCT_APEX_BOOT_JARS))
 
 $(call add_json_bool, VndkUseCoreVariant,                $(TARGET_VNDK_USE_CORE_VARIANT))
 $(call add_json_bool, VndkSnapshotBuildArtifacts,        $(VNDK_SNAPSHOT_BUILD_ARTIFACTS))
@@ -162,6 +164,7 @@
 $(call add_json_list, VendorSnapshotDirsExcluded,        $(VENDOR_SNAPSHOT_DIRS_EXCLUDED))
 $(call add_json_list, RecoverySnapshotDirsIncluded,      $(RECOVERY_SNAPSHOT_DIRS_INCLUDED))
 $(call add_json_list, RecoverySnapshotDirsExcluded,      $(RECOVERY_SNAPSHOT_DIRS_EXCLUDED))
+$(call add_json_bool, HostFakeSnapshotEnabled,           $(HOST_FAKE_SNAPSHOT_ENABLE))
 
 $(call add_json_bool, Treble_linker_namespaces,          $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
 $(call add_json_bool, Enforce_vintf_manifest,            $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST)))
@@ -198,6 +201,7 @@
 $(call add_json_str,  BoardSepolicyVers,                 $(BOARD_SEPOLICY_VERS))
 
 $(call add_json_str,  PlatformSepolicyVersion,           $(PLATFORM_SEPOLICY_VERSION))
+$(call add_json_str,  TotSepolicyVersion,                $(TOT_SEPOLICY_VERSION))
 
 $(call add_json_bool, Flatten_apex,                      $(filter true,$(TARGET_FLATTEN_APEX)))
 $(call add_json_bool, ForceApexSymlinkOptimization,      $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION)))
@@ -264,6 +268,9 @@
 
 $(call add_json_bool, SepolicySplit, $(filter true,$(PRODUCT_SEPOLICY_SPLIT)))
 
+$(call add_json_list, SepolicyFreezeTestExtraDirs,         $(SEPOLICY_FREEZE_TEST_EXTRA_DIRS))
+$(call add_json_list, SepolicyFreezeTestExtraPrebuiltDirs, $(SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS))
+
 $(call json_end)
 
 $(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index 1ebbf14..2c909ac 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -25,6 +25,15 @@
   LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_AAR)
 endif
 
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_HEADER_JAR)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_FULL_MANIFEST_FILE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEXPREOPT_CONFIG)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE)
+LOCAL_ADDITIONAL_CHECKED_MODULE += $(LOCAL_SOONG_DEX_JAR)
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
@@ -206,3 +215,8 @@
 		$(hide) touch $@)
 
 SOONG_ALREADY_CONV += $(LOCAL_MODULE)
+
+#######################################
+# Capture deps added after base_rules.mk
+include $(BUILD_NOTICE_FILE)
+#######################################
diff --git a/core/soong_rust_prebuilt.mk b/core/soong_rust_prebuilt.mk
index 26c099b..435a7d8 100644
--- a/core/soong_rust_prebuilt.mk
+++ b/core/soong_rust_prebuilt.mk
@@ -35,6 +35,9 @@
   endif
 endif
 
+# Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
+# to avoid checkbuilds making an extra copy of every module.
+LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
 
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/core/sysprop.mk b/core/sysprop.mk
index ec181f5..1d38f8c 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -98,7 +98,7 @@
     $(eval _option := --allow-dup)\
 )
 
-$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) $(3) $(6)
+$(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(3) $(6)
 	$(hide) echo Building $$@
 	$(hide) mkdir -p $$(dir $$@)
 	$(hide) rm -f $$@ && touch $$@
diff --git a/core/tasks/OWNERS b/core/tasks/OWNERS
new file mode 100644
index 0000000..594930d
--- /dev/null
+++ b/core/tasks/OWNERS
@@ -0,0 +1 @@
+per-file art-host-tests.mk = dshi@google.com,dsrbecky@google.com,jdesprez@google.com,rpl@google.com
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index fdd9591..876d77a 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -20,8 +20,207 @@
 include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
 
 .PHONY: cts
-cts: $(compatibility_zip)
-$(call dist-for-goals, cts, $(compatibility_zip))
+cts: $(compatibility_zip) $(compatibility_tests_list_zip)
+$(call dist-for-goals, cts, $(compatibility_zip) $(compatibility_tests_list_zip))
 
 .PHONY: cts_v2
 cts_v2: cts
+
+# platform version check (b/32056228)
+# ============================================================
+ifneq (,$(wildcard cts/))
+  cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt
+  cts_platform_version_string := $(shell cat $(cts_platform_version_path))
+  cts_platform_release_path := cts/tests/tests/os/assets/platform_releases.txt
+  cts_platform_release_string := $(shell cat $(cts_platform_release_path))
+
+  ifeq (,$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string)))
+    define error_msg
+      ============================================================
+      Could not find version "$(PLATFORM_VERSION)" in CTS platform version file:
+      $(cts_platform_version_path)
+      Most likely PLATFORM_VERSION in build/core/version_defaults.mk
+      has changed and a new version must be added to this CTS file.
+      ============================================================
+    endef
+    $(error $(error_msg))
+  endif
+  ifeq (,$(findstring $(PLATFORM_VERSION_LAST_STABLE),$(cts_platform_release_string)))
+    define error_msg
+      ============================================================
+      Could not find version "$(PLATFORM_VERSION_LAST_STABLE)" in CTS platform release file:
+      $(cts_platform_release_path)
+      Most likely PLATFORM_VERSION_LAST_STABLE in build/core/version_defaults.mk
+      has changed and a new version must be added to this CTS file.
+      ============================================================
+    endef
+    $(error $(error_msg))
+  endif
+endif
+
+# Creates a "cts-verifier" directory that will contain:
+#
+# 1. Out directory with a "android-cts-verifier" containing the CTS Verifier
+#    and other binaries it needs.
+#
+# 2. Zipped version of the android-cts-verifier directory to be included with
+#    the build distribution.
+##
+cts-dir := $(HOST_OUT)/cts-verifier
+verifier-dir-name := android-cts-verifier
+verifier-dir := $(cts-dir)/$(verifier-dir-name)
+verifier-zip-name := $(verifier-dir-name).zip
+verifier-zip := $(cts-dir)/$(verifier-zip-name)
+
+cts : $(verifier-zip)
+$(verifier-zip): PRIVATE_DIR := $(cts-dir)
+$(verifier-zip): $(SOONG_ANDROID_CTS_VERIFIER_ZIP)
+	rm -rf $(PRIVATE_DIR)
+	mkdir -p $(PRIVATE_DIR)
+	unzip -q -d $(PRIVATE_DIR) $<
+	$(copy-file-to-target)
+
+# For producing CTS coverage reports.
+# Run "make cts-test-coverage" in the $ANDROID_BUILD_TOP directory.
+
+cts_api_coverage_exe := $(HOST_OUT_EXECUTABLES)/cts-api-coverage
+dexdeps_exe := $(HOST_OUT_EXECUTABLES)/dexdeps
+
+coverage_out := $(HOST_OUT)/cts-api-coverage
+
+api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/api.xml
+
+napi_text_description := cts/tools/cts-api-coverage/etc/ndk-api.xml
+napi_xml_description := $(coverage_out)/ndk-api.xml
+$(napi_xml_description) : $(napi_text_description) $(ACP)
+		$(hide) echo "Preparing NDK API XML: $@"
+		$(hide) mkdir -p $(dir $@)
+		$(hide) $(ACP)  $< $@
+
+system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml
+
+cts-test-coverage-report := $(coverage_out)/test-coverage.html
+cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html
+cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml
+cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html
+cts-combined-coverage-report := $(coverage_out)/combined-coverage.html
+cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml
+
+cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)
+cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)
+
+android_cts_zip := $(HOST_OUT)/cts/android-cts.zip
+cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk
+
+$(cts-test-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-test-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-test-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-test-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-test-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-test-coverage-report) : $(android_cts_zip) $(cts_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\
+			$(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS System API Coverage Report",\
+			$(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\
+			$(PRIVATE_TEST_CASES),xml)
+
+$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk)
+$(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS Verifier API Coverage Report",\
+			$(PRIVATE_TEST_CASES),html)
+
+$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS Combined API Coverage Report",\
+			$(PRIVATE_TEST_CASES),html)
+
+$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
+$(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
+$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS Combined API Coverage Report - XML",\
+			$(PRIVATE_TEST_CASES),xml)
+
+.PHONY: cts-test-coverage
+cts-test-coverage : $(cts-test-coverage-report)
+
+.PHONY: cts-system-api-coverage
+cts-system-api-coverage : $(cts-system-api-coverage-report)
+
+.PHONY: cts-system-api-xml-coverage
+cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report)
+
+.PHONY: cts-verifier-coverage
+cts-verifier-coverage : $(cts-verifier-coverage-report)
+
+.PHONY: cts-combined-coverage
+cts-combined-coverage : $(cts-combined-coverage-report)
+
+.PHONY: cts-combined-xml-coverage
+cts-combined-xml-coverage : $(cts-combined-xml-coverage-report)
+
+.PHONY: cts-coverage-report-all cts-api-coverage
+cts-coverage-report-all: cts-test-coverage cts-verifier-coverage cts-combined-coverage cts-combined-xml-coverage
+
+# Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
+$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
+$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
+
+# Arguments;
+#  1 - Name of the report printed out on the screen
+#  2 - List of apk files that will be scanned to generate the report
+#  3 - Format of the report
+define generate-coverage-report-cts
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(PRIVATE_CTS_API_COVERAGE_EXE) -d $(PRIVATE_DEXDEPS_EXE) -a $(PRIVATE_API_XML_DESC) -n $(PRIVATE_NAPI_XML_DESC) -f $(3) -o $@ $(2)
+	@ echo $(1): file://$$(cd $(dir $@); pwd)/$(notdir $@)
+endef
+
+# Reset temp vars
+cts_api_coverage_dependencies :=
+cts_system_api_coverage_dependencies :=
+cts-combined-coverage-report :=
+cts-combined-xml-coverage-report :=
+cts-verifier-coverage-report :=
+cts-test-coverage-report :=
+cts-system-api-coverage-report :=
+cts-system-api-xml-coverage-report :=
+api_xml_description :=
+api_text_description :=
+system_api_xml_description :=
+napi_xml_description :=
+napi_text_description :=
+coverage_out :=
+dexdeps_exe :=
+cts_api_coverage_exe :=
+cts_verifier_apk :=
+android_cts_zip :=
diff --git a/core/tasks/dex_preopt_check.mk b/core/tasks/dex_preopt_check.mk
new file mode 100644
index 0000000..bfa1ec5
--- /dev/null
+++ b/core/tasks/dex_preopt_check.mk
@@ -0,0 +1,18 @@
+# Checks that some critical dexpreopt output files are installed.
+
+# Inputs:
+# DISABLE_DEXPREOPT_CHECK: True if the check should be disabled.
+# PRODUCT_PACKAGES: The list of packages to be installed for the product.
+# ALL_DEFAULT_INSTALLED_MODULES: The full list of modules going to be installed.
+# DEXPREOPT_SYSTEMSERVER_ARTIFACTS: The list of compilation artifacts of system server jars, which
+# 	is generated by Soong in dexpreopt_check.go.
+
+ifneq (true,$(DISABLE_DEXPREOPT_CHECK))
+  # Skip the check if the system server is not installed for the product.
+  ifneq (,$(filter services,$(PRODUCT_PACKAGES)))
+    $(call maybe-print-list-and-error,\
+      $(filter-out $(ALL_DEFAULT_INSTALLED_MODULES),$(DEXPREOPT_SYSTEMSERVER_ARTIFACTS)),\
+      Missing compilation artifacts. Dexpreopting is not working for some system server jars \
+    )
+  endif
+endif
diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index 4721591..f9ae2c1 100644
--- a/core/tasks/tools/build_custom_image.mk
+++ b/core/tasks/tools/build_custom_image.mk
@@ -57,7 +57,10 @@
 my_kernel_module_copy_files :=
 my_custom_image_modules_var := BOARD_$(strip $(call to-upper,$(my_custom_image_name)))_KERNEL_MODULES
 ifdef $(my_custom_image_modules_var)
-  my_kernel_module_copy_files += $(call build-image-kernel-modules,$(my_custom_image_modules_var),$(my_staging_dir),$(my_custom_image_name)/,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)))
+$(foreach kmod,\
+  $(call build-image-kernel-modules,$($(my_custom_image_modules_var)),$(my_staging_dir),$(CUSTOM_IMAGE_MOUNT_POINT),$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)),$($(my_custom_image_modules_var)),modules.load,,$(call intermediates-dir-for,PACKAGING,depmod_$(my_custom_image_name)_stripped)),\
+  $(eval pair := $(subst :,$(space),$(kmod)))\
+  $(eval my_kernel_module_copy_files += $(word 1,$(pair)):$(subst $(my_staging_dir)/,,$(word 2,$(pair)))))
 endif
 
 # Collect CUSTOM_IMAGE_COPY_FILES.
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index 7d08a2f..47cf440 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -80,13 +80,18 @@
 compatibility_zip_deps += $(test_suite_notice_txt)
 compatibility_zip_resources += $(test_suite_notice_txt)
 
+compatibility_tests_list_zip := $(out_dir)-tests_list.zip
+
 compatibility_zip := $(out_dir).zip
+$(compatibility_zip) : .KATI_IMPLICIT_OUTPUTS := $(compatibility_tests_list_zip)
 $(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir)
 $(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools)
 $(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name)
 $(compatibility_zip): PRIVATE_DYNAMIC_CONFIG := $(test_suite_dynamic_config)
 $(compatibility_zip): PRIVATE_RESOURCES := $(compatibility_zip_resources)
 $(compatibility_zip): PRIVATE_JDK := $(test_suite_jdk)
+$(compatibility_zip): PRIVATE_tests_list := $(out_dir)-tests_list
+$(compatibility_zip): PRIVATE_tests_list_zip := $(compatibility_tests_list_zip)
 $(compatibility_zip): $(compatibility_zip_deps) | $(ADB) $(ACP)
 # Make dir structure
 	mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases
@@ -99,6 +104,11 @@
 	$(SOONG_ZIP) -d -o $@.tmp -C $(dir $@) -l $@.list
 	$(MERGE_ZIPS) $@ $@.tmp $(PRIVATE_JDK)
 	rm -f $@.tmp
+# Build a list of tests
+	rm -f $(PRIVATE_tests_list)
+	$(hide) grep -e .*\\.config$$ $@.list | sed s%$(PRIVATE_OUT_DIR)/testcases/%%g > $(PRIVATE_tests_list)
+	$(SOONG_ZIP) -d -o $(PRIVATE_tests_list_zip) -j -f $(PRIVATE_tests_list)
+	rm -f $(PRIVATE_tests_list)
 
 # Reset all input variables
 test_suite_name :=
diff --git a/core/tasks/vts-core-tests.mk b/core/tasks/vts-core-tests.mk
index 95c4d24..3c838b5 100644
--- a/core/tasks/vts-core-tests.mk
+++ b/core/tasks/vts-core-tests.mk
@@ -44,7 +44,7 @@
 $(compatibility_zip): $(copy_kernel_tests)
 
 .PHONY: vts
-vts: $(compatibility_zip)
-$(call dist-for-goals, vts, $(compatibility_zip))
+vts: $(compatibility_zip) $(compatibility_tests_list_zip)
+$(call dist-for-goals, vts, $(compatibility_zip) $(compatibility_tests_list_zip))
 
 tests: vts
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index a5e40a0..3d4598e 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -39,51 +39,10 @@
   include $(INTERNAL_BUILD_ID_MAKEFILE)
 endif
 
-DEFAULT_PLATFORM_VERSION := SP2A
-MIN_PLATFORM_VERSION := SP2A
-MAX_PLATFORM_VERSION := SP2A
-
-ALLOWED_VERSIONS := $(call allowed-platform-versions,\
-  $(MIN_PLATFORM_VERSION),\
-  $(MAX_PLATFORM_VERSION),\
-  $(DEFAULT_PLATFORM_VERSION))
-
-ifndef TARGET_PLATFORM_VERSION
-  TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION)
-endif
-
-ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION)))
-  $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of)
-  $(error $(ALLOWED_VERSIONS))
-endif
-ALLOWED_VERSIONS :=
-MIN_PLATFORM_VERSION :=
-MAX_PLATFORM_VERSION :=
-
-.KATI_READONLY := \
-  DEFAULT_PLATFORM_VERSION \
-  TARGET_PLATFORM_VERSION
-
-# Default versions for each TARGET_PLATFORM_VERSION
-# TODO: PLATFORM_VERSION, PLATFORM_SDK_VERSION, etc. should be conditional
-# on this
-
-# This is the canonical definition of the platform version,
-# which is the version that we reveal to the end user.
-# Update this value when the platform version changes (rather
-# than overriding it somewhere else).  Can be an arbitrary string.
-
-# When you change PLATFORM_VERSION for a given PLATFORM_SDK_VERSION
-# please add that PLATFORM_VERSION as well as clean up obsolete PLATFORM_VERSION's
-# in the following text file:
-# cts/tests/tests/os/assets/platform_versions.txt
-
-# Note that there should be one PLATFORM_VERSION and PLATFORM_VERSION_CODENAME
-# entry for each unreleased API level, regardless of
-# MIN_PLATFORM_VERSION/MAX_PLATFORM_VERSION. PLATFORM_VERSION is used to
-# generate the range of allowed SDK versions, so it must have an entry for every
-# unreleased API level targetable by this branch, not just those that are valid
-# lunch targets for this branch.
+DEFAULT_PLATFORM_VERSION := TP1A
+.KATI_READONLY := DEFAULT_PLATFORM_VERSION
+MIN_PLATFORM_VERSION := TP1A
+MAX_PLATFORM_VERSION := TP1A
 
 # The last stable version name of the platform that was released.  During
 # development, this stays at that previous version, while the codename indicates
@@ -94,49 +53,7 @@
 # These are the current development codenames, if the build is not a final
 # release build.  If this is a final release build, it is simply "REL".
 PLATFORM_VERSION_CODENAME.SP2A := Sv2
-
-ifndef PLATFORM_VERSION_CODENAME
-  PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION))
-  ifndef PLATFORM_VERSION_CODENAME
-    # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION
-    PLATFORM_VERSION_CODENAME := $(TARGET_PLATFORM_VERSION)
-  endif
-
-  # This is all of the *active* development codenames.
-  # This confusing name is needed because
-  # all_codenames has been baked into build.prop for ages.
-  #
-  # Should be either the same as PLATFORM_VERSION_CODENAME or a comma-separated
-  # list of additional codenames after PLATFORM_VERSION_CODENAME.
-  PLATFORM_VERSION_ALL_CODENAMES :=
-
-  # Build a list of all active code names. Avoid duplicates, and stop when we
-  # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond
-  # that is not included in our build).
-  _versions_in_target := \
-    $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION))
-  $(foreach version,$(_versions_in_target),\
-    $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\
-    $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
-      $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename))))
-
-  # And convert from space separated to comma separated.
-  PLATFORM_VERSION_ALL_CODENAMES := \
-    $(subst $(space),$(comma),$(strip $(PLATFORM_VERSION_ALL_CODENAMES)))
-
-endif
-.KATI_READONLY := \
-  PLATFORM_VERSION_CODENAME \
-  PLATFORM_VERSION_ALL_CODENAMES
-
-ifndef PLATFORM_VERSION
-  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-      PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE)
-  else
-      PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME)
-  endif
-endif
-.KATI_READONLY := PLATFORM_VERSION
+PLATFORM_VERSION_CODENAME.TP1A := Tiramisu
 
 ifndef PLATFORM_SDK_VERSION
   # This is the canonical definition of the SDK version, which defines
@@ -155,84 +72,13 @@
 endif
 .KATI_READONLY := PLATFORM_SDK_VERSION
 
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-  PLATFORM_PREVIEW_SDK_VERSION := 0
-else
-  ifndef PLATFORM_PREVIEW_SDK_VERSION
-    # This is the definition of a preview SDK version over and above the current
-    # platform SDK version. Unlike the platform SDK version, a higher value
-    # for preview SDK version does NOT mean that all prior preview APIs are
-    # included. Packages reading this value to determine compatibility with
-    # known APIs should check that this value is precisely equal to the preview
-    # SDK version the package was built for, otherwise it should fall back to
-    # assuming the device can only support APIs as of the previous official
-    # public release.
-    # This value will always be forced to 0 for release builds by the logic
-    # in the "ifeq" block above, so the value below will be used on any
-    # non-release builds, and it should always be at least 1, to indicate that
-    # APIs may have changed since the claimed PLATFORM_SDK_VERSION.
-    PLATFORM_PREVIEW_SDK_VERSION := 1
-  endif
-endif
-.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION
+# This is the sdk extension version of this tree.
+PLATFORM_SDK_EXTENSION_VERSION := 1
+.KATI_READONLY := PLATFORM_SDK_EXTENSION_VERSION
 
-ifndef DEFAULT_APP_TARGET_SDK
-  # This is the default minSdkVersion and targetSdkVersion to use for
-  # all .apks created by the build system.  It can be overridden by explicitly
-  # setting these in the .apk's AndroidManifest.xml.  It is either the code
-  # name of the development build or, if this is a release build, the official
-  # SDK version of this release.
-  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-    DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)
-  else
-    DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)
-  endif
-endif
-.KATI_READONLY := DEFAULT_APP_TARGET_SDK
-
-ifndef PLATFORM_VNDK_VERSION
-  # This is the definition of the VNDK version for the current VNDK libraries.
-  # The version is only available when PLATFORM_VERSION_CODENAME == REL.
-  # Otherwise, it will be set to a CODENAME version. The ABI is allowed to be
-  # changed only before the Android version is released. Once
-  # PLATFORM_VNDK_VERSION is set to actual version, the ABI for this version
-  # will be frozon and emit build errors if any ABI for the VNDK libs are
-  # changed.
-  # After that the snapshot of the VNDK with this version will be generated.
-  #
-  # The VNDK version follows PLATFORM_SDK_VERSION.
-  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-    PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
-  else
-    PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
-  endif
-endif
-.KATI_READONLY := PLATFORM_VNDK_VERSION
-
-ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
-  # This is the oldest version of system SDK that the platform supports. Contrary
-  # to the public SDK where platform essentially supports all previous SDK versions,
-  # platform supports only a few number of recent system SDK versions as some of
-  # old system APIs are gradually deprecated, removed and then deleted.
-  PLATFORM_SYSTEMSDK_MIN_VERSION := 28
-endif
-.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION
-
-# This is the list of system SDK versions that the current platform supports.
-PLATFORM_SYSTEMSDK_VERSIONS :=
-ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION))
-  $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\
-    $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION)))
-  PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION))
-endif
-# Platform always supports the current version
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-  PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION)
-else
-  PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_VERSION_CODENAME)
-endif
-PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS)))
-.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS
+# This is the sdk extension version that PLATFORM_SDK_VERSION ships with.
+PLATFORM_BASE_SDK_EXTENSION_VERSION := 1
+.KATI_READONLY := PLATFORM_BASE_SDK_EXTENSION_VERSION
 
 ifndef PLATFORM_SECURITY_PATCH
     #  Used to indicate the security patch that has been applied to the device.
@@ -244,65 +90,5 @@
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
-ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP
-  # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH.
-  PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s)
-endif
-.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP
+include $(BUILD_SYSTEM)/version_util.mk
 
-ifndef PLATFORM_BASE_OS
-  # Used to indicate the base os applied to the device.
-  # Can be an arbitrary string, but must be a single word.
-  #
-  # If there is no $PLATFORM_BASE_OS set, keep it empty.
-  PLATFORM_BASE_OS :=
-endif
-.KATI_READONLY := PLATFORM_BASE_OS
-
-ifndef BUILD_ID
-  # Used to signify special builds.  E.g., branches and/or releases,
-  # like "M5-RC7".  Can be an arbitrary string, but must be a single
-  # word and a valid file name.
-  #
-  # If there is no BUILD_ID set, make it obvious.
-  BUILD_ID := UNKNOWN
-endif
-.KATI_READONLY := BUILD_ID
-
-ifndef BUILD_DATETIME
-  # Used to reproduce builds by setting the same time. Must be the number
-  # of seconds since the Epoch.
-  BUILD_DATETIME := $(shell date +%s)
-endif
-
-DATE := date -d @$(BUILD_DATETIME)
-.KATI_READONLY := DATE
-
-# Everything should be using BUILD_DATETIME_FROM_FILE instead.
-# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves
-# to soong_ui.
-$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)
-
-HAS_BUILD_NUMBER := true
-ifndef BUILD_NUMBER
-  # BUILD_NUMBER should be set to the source control value that
-  # represents the current state of the source code.  E.g., a
-  # perforce changelist number or a git hash.  Can be an arbitrary string
-  # (to allow for source control that uses something other than numbers),
-  # but must be a single word and a valid file name.
-  #
-  # If no BUILD_NUMBER is set, create a useful "I am an engineering build
-  # from this date/time" value.  Make it start with a non-digit so that
-  # anyone trying to parse it as an integer will probably get "0".
-  BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S)
-  HAS_BUILD_NUMBER := false
-endif
-.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER
-
-ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
-  # Used to set minimum supported target sdk version. Apps targeting sdk
-  # version lower than the set value will result in a warning being shown
-  # when any activity from the app is started.
-  PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23
-endif
-.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
diff --git a/core/version_util.mk b/core/version_util.mk
new file mode 100644
index 0000000..b7c4e48
--- /dev/null
+++ b/core/version_util.mk
@@ -0,0 +1,245 @@
+#
+# Copyright (C) 2008 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.
+#
+
+#
+
+ALLOWED_VERSIONS := $(call allowed-platform-versions,\
+  $(MIN_PLATFORM_VERSION),\
+  $(MAX_PLATFORM_VERSION),\
+  $(DEFAULT_PLATFORM_VERSION))
+
+ifndef TARGET_PLATFORM_VERSION
+  TARGET_PLATFORM_VERSION := $(DEFAULT_PLATFORM_VERSION)
+endif
+
+ifeq (,$(filter $(ALLOWED_VERSIONS), $(TARGET_PLATFORM_VERSION)))
+  $(warning Invalid TARGET_PLATFORM_VERSION '$(TARGET_PLATFORM_VERSION)', must be one of)
+  $(error $(ALLOWED_VERSIONS))
+endif
+ALLOWED_VERSIONS :=
+MIN_PLATFORM_VERSION :=
+MAX_PLATFORM_VERSION :=
+
+.KATI_READONLY := TARGET_PLATFORM_VERSION
+
+# Default versions for each TARGET_PLATFORM_VERSION
+# TODO: PLATFORM_VERSION, PLATFORM_SDK_VERSION, etc. should be conditional
+# on this
+
+# This is the canonical definition of the platform version,
+# which is the version that we reveal to the end user.
+# Update this value when the platform version changes (rather
+# than overriding it somewhere else).  Can be an arbitrary string.
+
+# When you change PLATFORM_VERSION for a given PLATFORM_SDK_VERSION
+# please add that PLATFORM_VERSION as well as clean up obsolete PLATFORM_VERSION's
+# in the following text file:
+# cts/tests/tests/os/assets/platform_versions.txt
+
+# Note that there should be one PLATFORM_VERSION and PLATFORM_VERSION_CODENAME
+# entry for each unreleased API level, regardless of
+# MIN_PLATFORM_VERSION/MAX_PLATFORM_VERSION. PLATFORM_VERSION is used to
+# generate the range of allowed SDK versions, so it must have an entry for every
+# unreleased API level targetable by this branch, not just those that are valid
+# lunch targets for this branch.
+
+ifndef PLATFORM_VERSION_CODENAME
+  PLATFORM_VERSION_CODENAME := $(PLATFORM_VERSION_CODENAME.$(TARGET_PLATFORM_VERSION))
+  ifndef PLATFORM_VERSION_CODENAME
+    # PLATFORM_VERSION_CODENAME falls back to TARGET_PLATFORM_VERSION
+    PLATFORM_VERSION_CODENAME := $(TARGET_PLATFORM_VERSION)
+  endif
+
+  # This is all of the *active* development codenames.
+  # This confusing name is needed because
+  # all_codenames has been baked into build.prop for ages.
+  #
+  # Should be either the same as PLATFORM_VERSION_CODENAME or a comma-separated
+  # list of additional codenames after PLATFORM_VERSION_CODENAME.
+  PLATFORM_VERSION_ALL_CODENAMES :=
+
+  # Build a list of all active code names. Avoid duplicates, and stop when we
+  # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond
+  # that is not included in our build).
+  _versions_in_target := \
+    $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION))
+  $(foreach version,$(_versions_in_target),\
+    $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\
+    $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
+      $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename))))
+
+  # And convert from space separated to comma separated.
+  PLATFORM_VERSION_ALL_CODENAMES := \
+    $(subst $(space),$(comma),$(strip $(PLATFORM_VERSION_ALL_CODENAMES)))
+
+endif
+.KATI_READONLY := \
+  PLATFORM_VERSION_CODENAME \
+  PLATFORM_VERSION_ALL_CODENAMES
+
+ifndef PLATFORM_VERSION
+  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+      PLATFORM_VERSION := $(PLATFORM_VERSION_LAST_STABLE)
+  else
+      PLATFORM_VERSION := $(PLATFORM_VERSION_CODENAME)
+  endif
+endif
+.KATI_READONLY := PLATFORM_VERSION
+
+
+ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+  PLATFORM_PREVIEW_SDK_VERSION := 0
+else
+  ifndef PLATFORM_PREVIEW_SDK_VERSION
+    # This is the definition of a preview SDK version over and above the current
+    # platform SDK version. Unlike the platform SDK version, a higher value
+    # for preview SDK version does NOT mean that all prior preview APIs are
+    # included. Packages reading this value to determine compatibility with
+    # known APIs should check that this value is precisely equal to the preview
+    # SDK version the package was built for, otherwise it should fall back to
+    # assuming the device can only support APIs as of the previous official
+    # public release.
+    # This value will always be forced to 0 for release builds by the logic
+    # in the "ifeq" block above, so the value below will be used on any
+    # non-release builds, and it should always be at least 1, to indicate that
+    # APIs may have changed since the claimed PLATFORM_SDK_VERSION.
+    PLATFORM_PREVIEW_SDK_VERSION := 1
+  endif
+endif
+.KATI_READONLY := PLATFORM_PREVIEW_SDK_VERSION
+
+ifndef DEFAULT_APP_TARGET_SDK
+  # This is the default minSdkVersion and targetSdkVersion to use for
+  # all .apks created by the build system.  It can be overridden by explicitly
+  # setting these in the .apk's AndroidManifest.xml.  It is either the code
+  # name of the development build or, if this is a release build, the official
+  # SDK version of this release.
+  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+    DEFAULT_APP_TARGET_SDK := $(PLATFORM_SDK_VERSION)
+  else
+    DEFAULT_APP_TARGET_SDK := $(PLATFORM_VERSION_CODENAME)
+  endif
+endif
+.KATI_READONLY := DEFAULT_APP_TARGET_SDK
+
+ifndef PLATFORM_VNDK_VERSION
+  # This is the definition of the VNDK version for the current VNDK libraries.
+  # The version is only available when PLATFORM_VERSION_CODENAME == REL.
+  # Otherwise, it will be set to a CODENAME version. The ABI is allowed to be
+  # changed only before the Android version is released. Once
+  # PLATFORM_VNDK_VERSION is set to actual version, the ABI for this version
+  # will be frozon and emit build errors if any ABI for the VNDK libs are
+  # changed.
+  # After that the snapshot of the VNDK with this version will be generated.
+  #
+  # The VNDK version follows PLATFORM_SDK_VERSION.
+  ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+    PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
+  else
+    PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
+  endif
+endif
+.KATI_READONLY := PLATFORM_VNDK_VERSION
+
+ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
+  # This is the oldest version of system SDK that the platform supports. Contrary
+  # to the public SDK where platform essentially supports all previous SDK versions,
+  # platform supports only a few number of recent system SDK versions as some of
+  # old system APIs are gradually deprecated, removed and then deleted.
+  PLATFORM_SYSTEMSDK_MIN_VERSION := 28
+endif
+.KATI_READONLY := PLATFORM_SYSTEMSDK_MIN_VERSION
+
+# This is the list of system SDK versions that the current platform supports.
+PLATFORM_SYSTEMSDK_VERSIONS :=
+ifneq (,$(PLATFORM_SYSTEMSDK_MIN_VERSION))
+  $(if $(call math_is_number,$(PLATFORM_SYSTEMSDK_MIN_VERSION)),,\
+    $(error PLATFORM_SYSTEMSDK_MIN_VERSION must be a number, but was $(PLATFORM_SYSTEMSDK_MIN_VERSION)))
+  PLATFORM_SYSTEMSDK_VERSIONS := $(call int_range_list,$(PLATFORM_SYSTEMSDK_MIN_VERSION),$(PLATFORM_SDK_VERSION))
+endif
+# Platform always supports the current version
+ifeq (REL,$(PLATFORM_VERSION_CODENAME))
+  PLATFORM_SYSTEMSDK_VERSIONS += $(PLATFORM_SDK_VERSION)
+else
+  PLATFORM_SYSTEMSDK_VERSIONS += $(subst $(comma),$(space),$(PLATFORM_VERSION_ALL_CODENAMES))
+endif
+PLATFORM_SYSTEMSDK_VERSIONS := $(strip $(sort $(PLATFORM_SYSTEMSDK_VERSIONS)))
+.KATI_READONLY := PLATFORM_SYSTEMSDK_VERSIONS
+
+.KATI_READONLY := PLATFORM_SECURITY_PATCH
+
+ifndef PLATFORM_SECURITY_PATCH_TIMESTAMP
+  # Used to indicate the matching timestamp for the security patch string in PLATFORM_SECURITY_PATCH.
+  PLATFORM_SECURITY_PATCH_TIMESTAMP := $(shell date -d 'TZ="GMT" $(PLATFORM_SECURITY_PATCH)' +%s)
+endif
+.KATI_READONLY := PLATFORM_SECURITY_PATCH_TIMESTAMP
+
+ifndef PLATFORM_BASE_OS
+  # Used to indicate the base os applied to the device.
+  # Can be an arbitrary string, but must be a single word.
+  #
+  # If there is no $PLATFORM_BASE_OS set, keep it empty.
+  PLATFORM_BASE_OS :=
+endif
+.KATI_READONLY := PLATFORM_BASE_OS
+
+ifndef BUILD_ID
+  # Used to signify special builds.  E.g., branches and/or releases,
+  # like "M5-RC7".  Can be an arbitrary string, but must be a single
+  # word and a valid file name.
+  #
+  # If there is no BUILD_ID set, make it obvious.
+  BUILD_ID := UNKNOWN
+endif
+.KATI_READONLY := BUILD_ID
+
+ifndef BUILD_DATETIME
+  # Used to reproduce builds by setting the same time. Must be the number
+  # of seconds since the Epoch.
+  BUILD_DATETIME := $(shell date +%s)
+endif
+
+DATE := date -d @$(BUILD_DATETIME)
+.KATI_READONLY := DATE
+
+# Everything should be using BUILD_DATETIME_FROM_FILE instead.
+# BUILD_DATETIME and DATE can be removed once BUILD_NUMBER moves
+# to soong_ui.
+$(KATI_obsolete_var BUILD_DATETIME,Use BUILD_DATETIME_FROM_FILE)
+
+HAS_BUILD_NUMBER := true
+ifndef BUILD_NUMBER
+  # BUILD_NUMBER should be set to the source control value that
+  # represents the current state of the source code.  E.g., a
+  # perforce changelist number or a git hash.  Can be an arbitrary string
+  # (to allow for source control that uses something other than numbers),
+  # but must be a single word and a valid file name.
+  #
+  # If no BUILD_NUMBER is set, create a useful "I am an engineering build
+  # from this date/time" value.  Make it start with a non-digit so that
+  # anyone trying to parse it as an integer will probably get "0".
+  BUILD_NUMBER := eng.$(shell echo $${BUILD_USERNAME:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S)
+  HAS_BUILD_NUMBER := false
+endif
+.KATI_READONLY := BUILD_NUMBER HAS_BUILD_NUMBER
+
+ifndef PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
+  # Used to set minimum supported target sdk version. Apps targeting sdk
+  # version lower than the set value will result in a warning being shown
+  # when any activity from the app is started.
+  PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23
+endif
+.KATI_READONLY := PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION
diff --git a/envsetup.sh b/envsetup.sh
index 8a995c7..4301d73 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -240,7 +240,7 @@
         export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2
     fi
 
-    export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin
+    export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools
 
     # add kernel specific binaries
     case $(uname -s) in
@@ -331,15 +331,15 @@
 
 function bazel()
 {
-    local T="$(gettop)"
-    if [ ! "$T" ]; then
-        echo "Couldn't locate the top of the tree.  Try setting TOP."
-        return
+    if which bazel &>/dev/null; then
+        >&2 echo "NOTE: bazel() function sourced from Android's envsetup.sh is being used instead of $(which bazel)"
+        >&2 echo
     fi
 
-    if which bazel &>/dev/null; then
-        >&2 echo "NOTE: bazel() function sourced from envsetup.sh is being used instead of $(which bazel)"
-        >&2 echo
+    local T="$(gettop)"
+    if [ ! "$T" ]; then
+        >&2 echo "Couldn't locate the top of the Android tree. Try setting TOP. This bazel() function cannot be used outside of the AOSP directory."
+        return
     fi
 
     "$T/tools/bazel" "$@"
@@ -703,6 +703,10 @@
     build_build_var_cache
     if [ $? -ne 0 ]
     then
+        if [[ "$product" =~ .*_(eng|user|userdebug) ]]
+        then
+            echo "Did you mean -${product/*_/}? (dash instead of underscore)"
+        fi
         return 1
     fi
     export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
@@ -1455,7 +1459,7 @@
         > $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1
 }
 
-# Verifies that module-info.txt exists, creating it if it doesn't.
+# Verifies that module-info.txt exists, returning nonzero if it doesn't.
 function verifymodinfo() {
     if [ ! "$ANDROID_PRODUCT_OUT" ]; then
         if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
@@ -1466,7 +1470,7 @@
 
     if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
         if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
-            echo "Could not find module-info.json. It will only be built once, and it can be updated with 'refreshmod'" >&2
+            echo "Could not find module-info.json. Please run 'refreshmod' first." >&2
         fi
         return 1
     fi
@@ -1585,6 +1589,10 @@
 function installmod() {
     if [[ $# -eq 0 ]]; then
         echo "usage: installmod [adb install arguments] <module>" >&2
+        echo "" >&2
+        echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
+        echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
+        echo "-s must be the first flag passed if it exists." >&2
         return 1
     fi
 
@@ -1599,9 +1607,18 @@
         echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
         return 1
     fi
+    local serial_device=""
+    if [[ "$1" == "-s" ]]; then
+        if [[ $# -le 2 ]]; then
+            echo "-s requires an argument" >&2
+            return 1
+        fi
+        serial_device="-s $2"
+        shift 2
+    fi
     local length=$(( $# - 1 ))
-    echo adb install ${@:1:$length} $_path
-    adb install ${@:1:$length} $_path
+    echo adb $serial_device install ${@:1:$length} $_path
+    adb $serial_device install ${@:1:$length} $_path
 }
 
 function _complete_android_module_names() {
@@ -1657,12 +1674,19 @@
     if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
         color_failed=$'\E'"[0;31m"
         color_success=$'\E'"[0;32m"
+        color_warning=$'\E'"[0;33m"
         color_reset=$'\E'"[00m"
     else
         color_failed=""
         color_success=""
         color_reset=""
     fi
+
+    if [[ "x${USE_RBE}" == "x" && $mins -gt 15 && "${ANDROID_BUILD_ENVIRONMENT_CONFIG}" == "googler" ]]; then
+        echo
+        echo "${color_warning}Start using RBE (http://go/build-fast) to get faster builds!${color_reset}"
+    fi
+
     echo
     if [ $ret -eq 0 ] ; then
         echo -n "${color_success}#### build completed successfully "
@@ -1687,7 +1711,23 @@
     if T="$(gettop)"; then
       _wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@"
     else
-      echo "Couldn't locate the top of the tree. Try setting TOP."
+      >&2 echo "Couldn't locate the top of the tree. Try setting TOP."
+      return 1
+    fi
+)
+
+# Convenience entry point (like m) to use Bazel in AOSP.
+function b()
+(
+    # Generate BUILD, bzl files into the synthetic Bazel workspace (out/soong/workspace).
+    _trigger_build "all-modules" bp2build USE_BAZEL_ANALYSIS= || return 1
+    # Then, run Bazel using the synthetic workspace as the --package_path.
+    if [[ -z "$@" ]]; then
+        # If there are no args, show help.
+        bazel help
+    else
+        # Else, always run with the bp2build configuration, which sets Bazel's package path to the synthetic workspace.
+        bazel "$@" --config=bp2build
     fi
 )
 
@@ -1843,6 +1883,16 @@
     fi
 }
 
+# Source necessary setup scripts needed to run the build with Remote Execution.
+function source_rbe() {
+    local T=$(gettop)
+
+    if [[ "x$USE_RBE" != "x" && "$USE_RBE" != "false" ]]; then
+        . $T/build/make/rbesetup.sh --skip-envsetup
+    fi
+}
+
 validate_current_shell
 source_vendorsetup
+source_rbe
 addcompletions
diff --git a/rbesetup.sh b/rbesetup.sh
index ec39e6e..3b0e7cf 100644
--- a/rbesetup.sh
+++ b/rbesetup.sh
@@ -24,8 +24,11 @@
 }
 
 # This function needs to run first as the remaining defining functions may be
-# using the envsetup.sh defined functions.
-_source_env_setup_script || return
+# using the envsetup.sh defined functions. Skip this part if this script is already
+# being invoked from envsetup.sh.
+if [[ "$1" != "--skip-envsetup" ]]; then
+  _source_env_setup_script || return
+fi
 
 # This function prefixes the given command with appropriate variables needed
 # for the build to be executed with RBE.
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 4dd6b17..142270e 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -24,8 +24,10 @@
 	$(call pretty,"Generated: ($@)")
 ifdef board_info_txt
 	$(hide) grep -v '#' $< > $@
-else
+else ifdef TARGET_BOOTLOADER_BOARD_NAME
 	$(hide) echo "board=$(TARGET_BOOTLOADER_BOARD_NAME)" > $@
+else
+	$(hide) echo "" > $@
 endif
 
 # Copy compatibility metadata to the device.
diff --git a/target/board/BoardConfigGkiCommon.mk b/target/board/BoardConfigGkiCommon.mk
index f480b93..c0f5db9 100644
--- a/target/board/BoardConfigGkiCommon.mk
+++ b/target/board/BoardConfigGkiCommon.mk
@@ -32,9 +32,6 @@
 BOARD_USES_RECOVERY_AS_BOOT :=
 TARGET_NO_KERNEL := false
 BOARD_USES_GENERIC_KERNEL_IMAGE := true
-BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := \
-    5.4-android12-unstable \
-    5.10-android12-unstable \
 
 # Copy boot image in $OUT to target files. This is defined for targets where
 # the installed GKI APEXes are built from source.
diff --git a/target/board/BoardConfigModuleCommon.mk b/target/board/BoardConfigModuleCommon.mk
index 9832474..24c01a5 100644
--- a/target/board/BoardConfigModuleCommon.mk
+++ b/target/board/BoardConfigModuleCommon.mk
@@ -4,7 +4,3 @@
 
 # Required for all module devices.
 TARGET_USES_64_BIT_BINDER := true
-
-# Necessary to make modules able to use the VNDK via 'use_vendor: true'
-# TODO(b/185769808): look into whether this is still used.
-BOARD_VNDK_VERSION := current
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index 49ae216..b0c9950 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -57,12 +57,6 @@
 
 BOARD_KERNEL-4.19-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
 BOARD_KERNEL-4.19-GZ-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4-GZ-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 47185920
-BOARD_KERNEL-5.4-LZ4_BOOTIMAGE_PARTITION_SIZE := 53477376
-BOARD_KERNEL-5.4-LZ4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 53477376
 BOARD_KERNEL-5.10_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.10-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.10-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
@@ -74,13 +68,11 @@
 
 BOARD_KERNEL_BINARIES := \
     kernel-4.19-gz \
-    kernel-5.4 kernel-5.4-gz kernel-5.4-lz4 \
     kernel-5.10 kernel-5.10-gz kernel-5.10-lz4 \
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 BOARD_KERNEL_BINARIES += \
     kernel-4.19-gz-allsyms \
-    kernel-5.4-allsyms kernel-5.4-gz-allsyms kernel-5.4-lz4-allsyms \
     kernel-5.10-allsyms kernel-5.10-gz-allsyms kernel-5.10-lz4-allsyms \
 
 endif
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index b331af4..0a05d9c 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -16,23 +16,16 @@
 
 PRODUCT_COPY_FILES += \
     kernel/prebuilts/4.19/arm64/kernel-4.19-gz:kernel-4.19-gz \
-    kernel/prebuilts/5.4/arm64/kernel-5.4:kernel-5.4 \
-    kernel/prebuilts/5.4/arm64/kernel-5.4-gz:kernel-5.4-gz \
-    kernel/prebuilts/5.4/arm64/kernel-5.4-lz4:kernel-5.4-lz4 \
     kernel/prebuilts/5.10/arm64/kernel-5.10:kernel-5.10 \
     kernel/prebuilts/5.10/arm64/kernel-5.10-gz:kernel-5.10-gz \
     kernel/prebuilts/5.10/arm64/kernel-5.10-lz4:kernel-5.10-lz4 \
 
 $(call dist-for-goals, dist_files, kernel/prebuilts/4.19/arm64/prebuilt-info.txt:kernel/4.19/prebuilt-info.txt)
-$(call dist-for-goals, dist_files, kernel/prebuilts/5.4/arm64/prebuilt-info.txt:kernel/5.4/prebuilt-info.txt)
 $(call dist-for-goals, dist_files, kernel/prebuilts/5.10/arm64/prebuilt-info.txt:kernel/5.10/prebuilt-info.txt)
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 PRODUCT_COPY_FILES += \
     kernel/prebuilts/4.19/arm64/kernel-4.19-gz-allsyms:kernel-4.19-gz-allsyms \
-    kernel/prebuilts/5.4/arm64/kernel-5.4-allsyms:kernel-5.4-allsyms \
-    kernel/prebuilts/5.4/arm64/kernel-5.4-gz-allsyms:kernel-5.4-gz-allsyms \
-    kernel/prebuilts/5.4/arm64/kernel-5.4-lz4-allsyms:kernel-5.4-lz4-allsyms \
     kernel/prebuilts/5.10/arm64/kernel-5.10-allsyms:kernel-5.10-allsyms \
     kernel/prebuilts/5.10/arm64/kernel-5.10-gz-allsyms:kernel-5.10-gz-allsyms \
     kernel/prebuilts/5.10/arm64/kernel-5.10-lz4-allsyms:kernel-5.10-lz4-allsyms \
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
index bdc862e..640216c 100755
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -28,19 +28,16 @@
 include build/make/target/board/BoardConfigGkiCommon.mk
 
 BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
-BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.10_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.10-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
 
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
 
 BOARD_KERNEL_BINARIES := \
-    kernel-5.4 \
     kernel-5.10 \
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 BOARD_KERNEL_BINARIES += \
-    kernel-5.4-allsyms \
     kernel-5.10-allsyms \
 
 endif
diff --git a/target/board/generic_x86_64/device.mk b/target/board/generic_x86_64/device.mk
index f31a491..d28ace7 100755
--- a/target/board/generic_x86_64/device.mk
+++ b/target/board/generic_x86_64/device.mk
@@ -15,15 +15,12 @@
 #
 
 PRODUCT_COPY_FILES += \
-    kernel/prebuilts/5.4/x86_64/kernel-5.4:kernel-5.4 \
     kernel/prebuilts/5.10/x86_64/kernel-5.10:kernel-5.10 \
 
-$(call dist-for-goals, dist_files, kernel/prebuilts/5.4/x86_64/prebuilt-info.txt:kernel/5.4/prebuilt-info.txt)
 $(call dist-for-goals, dist_files, kernel/prebuilts/5.10/x86_64/prebuilt-info.txt:kernel/5.10/prebuilt-info.txt)
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 PRODUCT_COPY_FILES += \
-    kernel/prebuilts/5.4/x86_64/kernel-5.4-allsyms:kernel-5.4-allsyms \
     kernel/prebuilts/5.10/x86_64/kernel-5.10-allsyms:kernel-5.10-allsyms \
 
 endif
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index 38f82a2..01897b7 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -53,6 +53,7 @@
 #
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
 
 #
 # Special settings for GSI releasing
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 5d78264..b3cfae4 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -56,6 +56,7 @@
 $(call inherit-product-if-exists, device/generic/goldfish/x86_64-vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
 
 #
 # Special settings for GSI releasing
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 14ce1af..68dd980 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -52,6 +52,7 @@
     com.android.adbd \
     com.android.appsearch \
     com.android.conscrypt \
+    com.android.cronet \
     com.android.extservices \
     com.android.i18n \
     com.android.ipsec \
@@ -132,6 +133,7 @@
     libaudioeffect_jni \
     libbinder \
     libbinder_ndk \
+    libbinder_rpc_unstable \
     libc.bootstrap \
     libcamera2ndk \
     libcutils \
@@ -215,7 +217,7 @@
     mtpd \
     ndc \
     netd \
-    NetworkStack \
+    NetworkStackNext \
     odsign \
     org.apache.http.legacy \
     otacerts \
@@ -325,7 +327,6 @@
     incident_report \
     ld.mc \
     lpdump \
-    mdnsd \
     minigzip \
     mke2fs \
     resize2fs \
@@ -373,6 +374,7 @@
     profcollectd \
     profcollectctl \
     remount \
+    servicedispatcher \
     showmap \
     sqlite3 \
     ss \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 07b3361..7622a69 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -78,7 +78,8 @@
 PRODUCT_PACKAGES += \
     vendor_compatibility_matrix.xml \
 
-# Packages to update the recovery partition, which will be installed on
-# /vendor. TODO(b/141648565): Don't install these unless they're needed.
+# Base modules and settings for the debug ramdisk, which is then packed
+# into a boot-debug.img and a vendor_boot-debug.img.
 PRODUCT_PACKAGES += \
-    applypatch
+    adb_debug.prop \
+    userdebug_plat_sepolicy.cil
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 0fa9058..071edbf 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -25,9 +25,8 @@
 # 4. Non-updatable APEX jars
 # 5. Updatable APEX jars
 #
-# ART APEX jars (1) are defined in ART_APEX_JARS. System, system_ext, and non updatable boot jars
-# are defined below in PRODUCT_BOOT_JARS. All updatable APEX boot jars are part of
-# PRODUCT_UPDATABLE_BOOT_JARS.
+# ART APEX jars (1) are defined in ART_APEX_JARS. System and system_ext boot jars are defined below
+# in PRODUCT_BOOT_JARS. All other non-art APEX boot jars are part of the PRODUCT_APEX_BOOT_JARS.
 #
 # The actual runtime ordering matching above is determined by derive_classpath service at runtime.
 # See packages/modules/SdkExtensions/README.md for more details.
@@ -45,14 +44,12 @@
     voip-common \
     ims-common
 
-# Non-updatable APEX jars. Keep the list sorted.
-PRODUCT_BOOT_JARS += \
-    com.android.i18n:core-icu4j
-
-# Updatable APEX boot jars. Keep the list sorted by module names and then library names.
-PRODUCT_UPDATABLE_BOOT_JARS := \
+# APEX boot jars. Keep the list sorted by module names and then library names.
+# Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage.
+PRODUCT_APEX_BOOT_JARS := \
     com.android.appsearch:framework-appsearch \
     com.android.conscrypt:conscrypt \
+    com.android.i18n:core-icu4j \
     com.android.ipsec:android.net.ipsec.ike \
     com.android.media:updatable-media \
     com.android.mediaprovider:framework-mediaprovider \
@@ -65,9 +62,10 @@
     com.android.tethering:framework-tethering \
     com.android.wifi:framework-wifi
 
-# Updatable APEX system server jars. Keep the list sorted by module names and then library names.
-PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS := \
+# APEX system server jars. Keep the list sorted by module names and then library names.
+PRODUCT_APEX_SYSTEM_SERVER_JARS := \
     com.android.appsearch:service-appsearch \
+    com.android.art:service-art \
     com.android.media:service-media-s \
     com.android.permission:service-permission \
 
diff --git a/target/product/emulator_vendor.mk b/target/product/emulator_vendor.mk
index b9f33ab..f71b275 100644
--- a/target/product/emulator_vendor.mk
+++ b/target/product/emulator_vendor.mk
@@ -37,7 +37,7 @@
 
 #watchdog tiggers reboot because location service is not
 #responding, disble it for now.
-#still keep it on internal master as it is still working
+#still keep it on internal main (master) as it is still working
 #once it is fixed in aosp, remove this block of comment.
 #PRODUCT_VENDOR_PROPERTIES += \
 #config.disable_location=true
diff --git a/target/product/generic_ramdisk.mk b/target/product/generic_ramdisk.mk
index ae81329..80d34be 100644
--- a/target/product/generic_ramdisk.mk
+++ b/target/product/generic_ramdisk.mk
@@ -25,6 +25,7 @@
 
 # Debug ramdisk
 PRODUCT_PACKAGES += \
+    adb_debug.prop \
     userdebug_plat_sepolicy.cil \
 
 _my_paths := \
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index cb4fdcb..39848e5 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -237,6 +237,6 @@
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := init
+LOCAL_MODULE_RELATIVE_PATH := gsi
 
 include $(BUILD_PREBUILT)
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 971ec92..399652c 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -18,8 +18,11 @@
 LLNDK: libsync.so
 LLNDK: libvndksupport.so
 LLNDK: libvulkan.so
+VNDK-SP: android.hardware.common-V2-ndk.so
 VNDK-SP: android.hardware.common-V2-ndk_platform.so
+VNDK-SP: android.hardware.common.fmq-V1-ndk.so
 VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so
+VNDK-SP: android.hardware.graphics.common-V2-ndk.so
 VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so
 VNDK-SP: android.hardware.graphics.common@1.0.so
 VNDK-SP: android.hardware.graphics.common@1.1.so
@@ -57,41 +60,80 @@
 VNDK-SP: libutilscallstack.so
 VNDK-SP: libz.so
 VNDK-core: android.hardware.audio.common@2.0.so
+VNDK-core: android.hardware.authsecret-V1-ndk.so
 VNDK-core: android.hardware.authsecret-V1-ndk_platform.so
+VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so
 VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk_platform.so
 VNDK-core: android.hardware.configstore-utils.so
 VNDK-core: android.hardware.configstore@1.0.so
 VNDK-core: android.hardware.configstore@1.1.so
 VNDK-core: android.hardware.confirmationui-support-lib.so
+VNDK-core: android.hardware.gnss-V1-ndk.so
 VNDK-core: android.hardware.gnss-V1-ndk_platform.so
 VNDK-core: android.hardware.graphics.allocator@2.0.so
 VNDK-core: android.hardware.graphics.allocator@3.0.so
 VNDK-core: android.hardware.graphics.allocator@4.0.so
 VNDK-core: android.hardware.graphics.bufferqueue@1.0.so
 VNDK-core: android.hardware.graphics.bufferqueue@2.0.so
+VNDK-core: android.hardware.health-V1-ndk.so
+VNDK-core: android.hardware.health.storage-V1-ndk.so
 VNDK-core: android.hardware.health.storage-V1-ndk_platform.so
+VNDK-core: android.hardware.identity-V3-ndk.so
 VNDK-core: android.hardware.identity-V3-ndk_platform.so
+VNDK-core: android.hardware.keymaster-V3-ndk.so
 VNDK-core: android.hardware.keymaster-V3-ndk_platform.so
+VNDK-core: android.hardware.light-V1-ndk.so
 VNDK-core: android.hardware.light-V1-ndk_platform.so
 VNDK-core: android.hardware.media.bufferpool@2.0.so
 VNDK-core: android.hardware.media.omx@1.0.so
 VNDK-core: android.hardware.media@1.0.so
+VNDK-core: android.hardware.memtrack-V1-ndk.so
 VNDK-core: android.hardware.memtrack-V1-ndk_platform.so
 VNDK-core: android.hardware.memtrack@1.0.so
+VNDK-core: android.hardware.oemlock-V1-ndk.so
 VNDK-core: android.hardware.oemlock-V1-ndk_platform.so
+VNDK-core: android.hardware.power-V2-ndk.so
 VNDK-core: android.hardware.power-V2-ndk_platform.so
+VNDK-core: android.hardware.power.stats-V1-ndk.so
 VNDK-core: android.hardware.power.stats-V1-ndk_platform.so
+VNDK-core: android.hardware.radio-V1-ndk.so
+VNDK-core: android.hardware.radio-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.config-V1-ndk.so
+VNDK-core: android.hardware.radio.config-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.data-V1-ndk.so
+VNDK-core: android.hardware.radio.data-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.messaging-V1-ndk.so
+VNDK-core: android.hardware.radio.messaging-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.modem-V1-ndk.so
+VNDK-core: android.hardware.radio.modem-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.network-V1-ndk.so
+VNDK-core: android.hardware.radio.network-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.sim-V1-ndk.so
+VNDK-core: android.hardware.radio.sim-V1-ndk_platform.so
+VNDK-core: android.hardware.radio.voice-V1-ndk.so
+VNDK-core: android.hardware.radio.voice-V1-ndk_platform.so
+VNDK-core: android.hardware.rebootescrow-V1-ndk.so
 VNDK-core: android.hardware.rebootescrow-V1-ndk_platform.so
+VNDK-core: android.hardware.security.keymint-V1-ndk.so
 VNDK-core: android.hardware.security.keymint-V1-ndk_platform.so
+VNDK-core: android.hardware.security.secureclock-V1-ndk.so
 VNDK-core: android.hardware.security.secureclock-V1-ndk_platform.so
+VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so
 VNDK-core: android.hardware.security.sharedsecret-V1-ndk_platform.so
 VNDK-core: android.hardware.soundtrigger@2.0-core.so
 VNDK-core: android.hardware.soundtrigger@2.0.so
+VNDK-core: android.hardware.vibrator-V2-ndk.so
 VNDK-core: android.hardware.vibrator-V2-ndk_platform.so
+VNDK-core: android.hardware.weaver-V1-ndk.so
 VNDK-core: android.hardware.weaver-V1-ndk_platform.so
+VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so
+VNDK-core: android.hardware.wifi.hostapd-V1-ndk_platform.so
 VNDK-core: android.hidl.token@1.0-utils.so
 VNDK-core: android.hidl.token@1.0.so
+VNDK-core: android.system.keystore2-V1-ndk.so
 VNDK-core: android.system.keystore2-V1-ndk_platform.so
+VNDK-core: android.system.suspend-V1-ndk.so
+VNDK-core: android.system.suspend-V1-ndk_platform.so
 VNDK-core: android.system.suspend@1.0.so
 VNDK-core: libaudioroute.so
 VNDK-core: libaudioutils.so
diff --git a/target/product/gsi/init.gsi.rc b/target/product/gsi/init.gsi.rc
index f482843..69c8e46 100644
--- a/target/product/gsi/init.gsi.rc
+++ b/target/product/gsi/init.gsi.rc
@@ -2,4 +2,4 @@
 # Android init script for GSI required initialization
 #
 
-import /system/system_ext/etc/init/init.vndk-${ro.vndk.version:-nodef}.rc
+import /system/system_ext/etc/gsi/init.vndk-${ro.vndk.version:-nodef}.rc
diff --git a/target/product/gsi/init.vndk-nodef.rc b/target/product/gsi/init.vndk-nodef.rc
index efeef11..1b141a0 100644
--- a/target/product/gsi/init.vndk-nodef.rc
+++ b/target/product/gsi/init.vndk-nodef.rc
@@ -1,3 +1,3 @@
 on early-init
-    # Must define BOARD_VNDK_VERSION
+    # Reboot if BOARD_VNDK_VERSION is not defined
     exec - root -- /system/bin/reboot bootloader
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index 09417b5..aaeefcb 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -71,10 +71,13 @@
 
 # Do not build non-GSI partition images.
 PRODUCT_BUILD_CACHE_IMAGE := false
+PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false
+PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false
 PRODUCT_BUILD_USERDATA_IMAGE := false
 PRODUCT_BUILD_VENDOR_IMAGE := false
 PRODUCT_BUILD_SUPER_PARTITION := false
 PRODUCT_BUILD_SUPER_EMPTY_IMAGE := false
+PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST := true
 
 # Always build modules from source
 MODULE_BUILD_FROM_SOURCE := true
diff --git a/target/product/non_ab_device.mk b/target/product/non_ab_device.mk
new file mode 100644
index 0000000..6dc4506
--- /dev/null
+++ b/target/product/non_ab_device.mk
@@ -0,0 +1,5 @@
+# Packages to update the recovery partition, which will be installed on
+# /vendor. Don't install these unless they're needed.
+PRODUCT_PACKAGES += \
+    applypatch
+
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index b511aa6..301605a 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -93,7 +93,7 @@
     dalvik.vm.appimageformat=lz4
 
 PRODUCT_SYSTEM_PROPERTIES += \
-    ro.dalvik.vm.native.bridge=0
+    ro.dalvik.vm.native.bridge?=0
 
 # Different dexopt types for different package update/install times.
 # On eng builds, make "boot" reasons only extract for faster turnaround.
@@ -127,10 +127,6 @@
     pm.dexopt.cmdline?=verify \
     pm.dexopt.shared?=speed
 
-# Pass file with the list of updatable boot class path packages to dex2oat.
-PRODUCT_SYSTEM_PROPERTIES += \
-    dalvik.vm.dex2oat-updatable-bcp-packages-file=/system/etc/updatable-bcp-packages.txt
-
 # Enable resolution of startup const strings.
 PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.dex2oat-resolve-startup-strings=true
diff --git a/target/product/sdk_phone_arm64.mk b/target/product/sdk_phone_arm64.mk
index 0831b54..4203d45 100644
--- a/target/product/sdk_phone_arm64.mk
+++ b/target/product/sdk_phone_arm64.mk
@@ -50,10 +50,6 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm64/device.mk)
 
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product, sdk/build/product_sdk.mk)
-$(call inherit-product, development/build/product_sdk.mk)
-
 # keep this apk for sdk targets for now
 PRODUCT_PACKAGES += \
     EmulatorSmokeTests
diff --git a/target/product/sdk_phone_armv7.mk b/target/product/sdk_phone_armv7.mk
index f649980..6c88b44 100644
--- a/target/product/sdk_phone_armv7.mk
+++ b/target/product/sdk_phone_armv7.mk
@@ -49,10 +49,6 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_arm/device.mk)
 
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product, sdk/build/product_sdk.mk)
-$(call inherit-product, development/build/product_sdk.mk)
-
 # keep this apk for sdk targets for now
 PRODUCT_PACKAGES += \
     EmulatorSmokeTests
diff --git a/target/product/sdk_phone_x86.mk b/target/product/sdk_phone_x86.mk
index 0e1bca4..a324e5f 100644
--- a/target/product/sdk_phone_x86.mk
+++ b/target/product/sdk_phone_x86.mk
@@ -49,10 +49,6 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86/device.mk)
 
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product-if-exists, sdk/build/product_sdk.mk)
-$(call inherit-product-if-exists, development/build/product_sdk.mk)
-
 # Overrides
 PRODUCT_BRAND := Android
 PRODUCT_NAME := sdk_phone_x86
diff --git a/target/product/sdk_phone_x86_64.mk b/target/product/sdk_phone_x86_64.mk
index fffac04..ff9018d 100644
--- a/target/product/sdk_phone_x86_64.mk
+++ b/target/product/sdk_phone_x86_64.mk
@@ -50,10 +50,6 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/emulator_vendor.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/board/emulator_x86_64/device.mk)
 
-# Define the host tools and libs that are parts of the SDK.
-$(call inherit-product-if-exists, sdk/build/product_sdk.mk)
-$(call inherit-product-if-exists, development/build/product_sdk.mk)
-
 # Overrides
 PRODUCT_BRAND := Android
 PRODUCT_NAME := sdk_phone_x86_64
diff --git a/target/product/security/README b/target/product/security/README
index 2b161bb..4ad5236 100644
--- a/target/product/security/README
+++ b/target/product/security/README
@@ -16,6 +16,7 @@
   development/tools/make_key shared        '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
   development/tools/make_key media         '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
   development/tools/make_key cts_uicc_2021 '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
+  development/tools/make_key fsverity      '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
 
 signing using the openssl commandline (for boot/system images)
 --------------------------------------------------------------
diff --git a/target/product/security/fsverity.pk8 b/target/product/security/fsverity.pk8
new file mode 100644
index 0000000..5bb69dc
--- /dev/null
+++ b/target/product/security/fsverity.pk8
Binary files differ
diff --git a/target/product/security/fsverity.x509.pem b/target/product/security/fsverity.x509.pem
new file mode 100644
index 0000000..b29c711
--- /dev/null
+++ b/target/product/security/fsverity.x509.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIECzCCAvOgAwIBAgIUDkPsN3C2kwiPnOnNZiHrK5S6oqowDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMB4XDTIxMTAxMjA0MzUyMFoXDTQ5MDIyNzA0MzUyMFowgZQxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp
+biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD
+VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1N8ro0RTY2Cl91daJvjo
+tDLjHrwrzSAQaVpEXGddPJYs0m8ej3Oh7Hbo4+ju36CIjgH9xDgpOb9LeTUMSXLF
+9Rlkdhz4VJlvaQuYz10FoqkvQo2/IsD2pAq3EktOHexfXCG8fhdCaVkayAuKX5ou
++RchZWCPwVhBx6fbpZeGhkFg6f7CwPSMEJ5DNtvHUieny8OwIbml0NILQjavP4nU
+GGJxkyKgodUYCdnOSE7FCUv875Op9e0ryTPvUZhKHPoRMe5enEgfq/WXVdqLhifF
+k6gYelcfq1bFRpwBm5KntX1b39V52vYUqXM8gD8Wy5RNo+aF0msJ6aBVcYeQsMlY
+4QIDAQABo1MwUTAdBgNVHQ4EFgQURbNJabjEzJ2CZzqIrX/ppnDM9l4wHwYDVR0j
+BBgwFoAURbNJabjEzJ2CZzqIrX/ppnDM9l4wDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAQEAl3eEb9xzlwAG31WKorYzflvFLX+LSuVMN3FEcZBcCXsW
++5QPfyvbJ2AgBzJmuH4XeGH0PebgLQN3PA4p9M0ZgXcHf4KBrSOMfpwUsFiTiD+z
+9KJxr4MTyXyFxO3rVlVCg/za0V8om2cRWsOb2TPRu8qeUSIT4yIj/pOXmz66b4xL
+5fKCuI7khRADCRnwyhPD9/f2/udB6qYx2MvDRchHMLqLvCzHJPS4gjhDTJJSo/st
+/GKqHWspHl5IbpRNlQci1ncc1RLub5gxPwlkIcNlOcziD+eYWeSn5B7v+5uIqxdP
+VY+WltSg4FEEzKFMjzfNpk1Uz+J6h2bi3VS0WZXdXQ==
+-----END CERTIFICATE-----
diff --git a/target/product/virtual_ab_ota/compression_with_xor.mk b/target/product/virtual_ab_ota/compression_with_xor.mk
new file mode 100644
index 0000000..7d92532
--- /dev/null
+++ b/target/product/virtual_ab_ota/compression_with_xor.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2021 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.
+#
+
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)
+
+
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true
diff --git a/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
index bc81b33..de1f07d 100644
--- a/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
+++ b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
@@ -24,3 +24,4 @@
 PRODUCT_PACKAGES += \
     linker.vendor_ramdisk \
     e2fsck.vendor_ramdisk \
+    fsck.f2fs.vendor_ramdisk \
diff --git a/tests/device.rbc b/tests/device.rbc
index 5d4e70c..37c5d0c 100644
--- a/tests/device.rbc
+++ b/tests/device.rbc
@@ -21,7 +21,13 @@
 ### PRODUCT_COPY_FILES += device_from:device_to
 ### include $(LOCAL_PATH)/include1.mk
 ### PRODUCT_PACKAGES += dev_after
-### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz
+### PRODUCT_COPY_FILES += $(call find-copy-subdir-files,audio_platform_info*.xml,device/google/redfin/audio,$(TARGET_COPY_OUT_VENDOR)/etc) xyz:/etc/xyz
+### PRODUCT_COPY_FILES += $(call copy-files,x.xml y.xml,/etc)
+### $(call add_soong_config_namespace,NS1)
+### $(call soong_config_append,NS1,v1,abc)
+### $(call soong_config_append,NS1,v2,def)
+### $(call add_soong_config_var_value,NS2,v3,abc)
+### $(call soong_config_set,NS2,v3,xyz)
 
 load("//build/make/core:product_config.rbc", "rblf")
 load(":part1.rbc", _part1_init = "init")
@@ -38,5 +44,25 @@
   cfg["PRODUCT_COPY_FILES"] += ["device_from:device_to"]
   _include1_init(g, handle)
   cfg["PRODUCT_PACKAGES"] += ["dev_after"]
-  cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin/audio", "||VENDOR-PATH-PH||/etc") +
-      ["xyz"])
+  cfg["PRODUCT_COPY_FILES"] += (rblf.find_and_copy("audio_platform_info*.xml", "device/google/redfin", "||VENDOR-PATH-PH||/etc") +
+      ["xyz:/etc/xyz"])
+  cfg["PRODUCT_COPY_FILES"] += rblf.copy_files("x.xml y.xml", "/etc")
+  cfg["PRODUCT_COPY_FILES"] += rblf.copy_files(["from/sub/x", "from/sub/y"], "to")
+
+  rblf.soong_config_namespace(g, "NS1")
+  rblf.soong_config_append(g, "NS1", "v1", "abc")
+  rblf.soong_config_append(g, "NS1", "v2", "def")
+  rblf.soong_config_set(g, "NS2", "v3", "abc")
+  rblf.soong_config_set(g, "NS2", "v3", "xyz")
+
+  if rblf.board_platform_in(g, "board1 board2"):
+    cfg["PRODUCT_PACKAGES"] += ["bad_package"]
+  g["TARGET_BOARD_PLATFORM"] = "board1"
+  if rblf.board_platform_in(g, "board1 board2"):
+    cfg["PRODUCT_PACKAGES"] += ["board1_in"]
+  if rblf.board_platform_in(g, ["board3","board2"]):
+    cfg["PRODUCT_PACKAGES"] += ["bad_board_in"]
+  if rblf.board_platform_is(g, "board1"):
+    cfg["PRODUCT_PACKAGES"] += ["board1_is"]
+  if rblf.board_platform_is(g, "board2"):
+    cfg["PRODUCT_PACKAGES"] += ["bad_board1_is"]
diff --git a/tests/part1.rbc b/tests/part1.rbc
index 3e50751..ae79d32 100644
--- a/tests/part1.rbc
+++ b/tests/part1.rbc
@@ -26,3 +26,5 @@
   cfg["PRODUCT_COPY_FILES"] += ["part_from:part_to"]
   rblf.setdefault(handle, "PRODUCT_PRODUCT_PROPERTIES")
   cfg["PRODUCT_PRODUCT_PROPERTIES"] += ["part_properties"]
+  rblf.soong_config_namespace(g, "NS1")
+  rblf.soong_config_append(g, "NS1", "v1", "abc_part1")
diff --git a/tests/run.rbc b/tests/run.rbc
index b13f835..3bb9b55 100644
--- a/tests/run.rbc
+++ b/tests/run.rbc
@@ -1,4 +1,3 @@
-
 # Copyright 2021 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,29 +21,76 @@
 #  * all runtime functions (wildcard, regex, etc.) work
 
 load("//build/make/core:product_config.rbc", "rblf")
+load(":version_defaults.rbc", "version_defaults")
 load(":device.rbc", "init")
 
 def assert_eq(expected, actual):
     if expected != actual:
-        fail("Expected %s, got %s" % (expected, actual))
+        fail("Expected '%s', got '%s'" % (expected, actual))
 
+# Unit tests for non-trivial runtime functions
+assert_eq("", rblf.mkstrip(" \n \t    "))
+assert_eq("a b c", rblf.mkstrip("  a b   \n  c \t"))
+assert_eq(1, rblf.mkstrip(1))
 
-globals, config = rblf.product_configuration("test/device", init)
+assert_eq("b1 b2", rblf.mksubst("a", "b", "a1 a2"))
+assert_eq(["b1", "x2"], rblf.mksubst("a", "b", ["a1", "x2"]))
+
+assert_eq("ABcdYZ", rblf.mkpatsubst("ab%yz", "AB%YZ", "abcdyz"))
+assert_eq("bcz", rblf.mkpatsubst("a%z", "A%Z", "bcz"))
+assert_eq(["Ay", "Az"], rblf.mkpatsubst("a%", "A%", ["ay", "az"]))
+assert_eq("AcZ bcz", rblf.mkpatsubst("a%z", "A%Z", "acz  bcz"))
+assert_eq("Abcd", rblf.mkpatsubst("a%", "A%", "abcd"))
+assert_eq("abcZ", rblf.mkpatsubst("%z", "%Z", "abcz"))
+assert_eq("azx b", rblf.mkpatsubst("az", "AZ", "azx  b"))
+assert_eq(["azx", "b"], rblf.mkpatsubst("az", "AZ", ["azx", "b"]))
+assert_eq("ABC", rblf.mkpatsubst("abc", "ABC", "abc"))
+assert_eq(["%/foo"], rblf.mkpatsubst("%", "\\%/%", ["foo"]))
+assert_eq(["foo/%"], rblf.mkpatsubst("%", "%/%", ["foo"]))
+assert_eq(["from/a:to/a", "from/b:to/b"], rblf.product_copy_files_by_pattern("from/%", "to/%", "a b"))
+
+assert_eq([], rblf.filter(["a", "", "b"], "f"))
+assert_eq(["", "b"], rblf.filter_out(["a", "" ], ["a", "", "b"] ))
+
+(globals, config, globals_base) = rblf.product_configuration("test/device", init, version_defaults)
 assert_eq(
     {
       "PRODUCT_COPY_FILES": [
           "part_from:part_to",
           "device_from:device_to",
-          "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio_platform_info_noextcodec_snd.xml",
-          "xyz"
+          "device/google/redfin/audio/audio_platform_info_noextcodec_snd.xml:||VENDOR-PATH-PH||/etc/audio/audio_platform_info_noextcodec_snd.xml",
+          "xyz:/etc/xyz",
+          "x.xml:/etc/x.xml",
+          "y.xml:/etc/y.xml",
+          "from/sub/x:to/x",
+          "from/sub/y:to/y",
       ],
       "PRODUCT_HOST_PACKAGES": ["host"],
       "PRODUCT_PACKAGES": [
           "dev",
           "inc",
-          "dev_after"
+          "dev_after",
+          "board1_in",
+          "board1_is",
       ],
       "PRODUCT_PRODUCT_PROPERTIES": ["part_properties"]
     },
     { k:v for k, v in sorted(config.items()) }
 )
+
+ns = globals["$SOONG_CONFIG_NAMESPACES"]
+assert_eq(
+    {
+        "NS1": {
+            "v1": "abc abc_part1",
+            "v2": "def"
+        },
+        "NS2": {
+            "v3": "xyz"
+        }
+    },
+    {k:v for k, v in sorted(ns.items()) }
+)
+
+assert_eq("S", globals["PLATFORM_VERSION"])
+assert_eq(30, globals["PLATFORM_SDK_VERSION"])
diff --git a/tests/version_defaults.rbc b/tests/version_defaults.rbc
new file mode 100644
index 0000000..9b35b57
--- /dev/null
+++ b/tests/version_defaults.rbc
@@ -0,0 +1,11 @@
+version_defaults = struct(
+    codenames = { "SP1A" : "S" },
+    default_platform_version = "SP1A",
+    max_platform_version = "SP1A",
+    min_platform_version = "SP1A",
+    platform_base_sdk_extension_version = 0,
+    platform_sdk_extension_version = 1,
+    platform_sdk_version = 30,
+    platform_security_patch = "2021-08-05",
+    platform_version_last_stable = 11,
+)
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
new file mode 100644
index 0000000..75b0de6
--- /dev/null
+++ b/tools/BUILD.bazel
@@ -0,0 +1,20 @@
+py_library(
+    name="event_log_tags",
+    srcs = ["event_log_tags.py"],
+)
+
+py_binary(
+    name="java-event-log-tags",
+    srcs=["java-event-log-tags.py"],
+    deps=[":event_log_tags"],
+    visibility = ["//visibility:public"],
+    python_version = "PY2",
+)
+
+py_binary(
+    name="merge-event-log-tags",
+    srcs=["merge-event-log-tags.py"],
+    deps=[":event_log_tags"],
+    visibility = ["//visibility:public"],
+    python_version = "PY2",
+)
diff --git a/tools/canoninja/README.md b/tools/canoninja/README.md
new file mode 100644
index 0000000..506acf7
--- /dev/null
+++ b/tools/canoninja/README.md
@@ -0,0 +1,151 @@
+# Ninja File Canonicalizer
+
+Suppose we have a tool that generates a Ninja file from some other description (think Kati and makefiles), and during
+the testing we discovered a regression. Furthermore, suppose that the generated Ninja file is large (think millions of
+lines). And, the new Ninja file has build statements and rules in a slightly different order. As the tool generates the
+rule names, the real differences in the output of the `diff` command are drowned in noise. Enter Canoninja.
+
+Canoninja renames each Ninja rule to the hash of its contents. After that, we can just sort the build statements, and a
+simple `comm` command immediately reveal the essential difference between the files.
+
+## Example
+
+Consider the following makefile
+
+```makefile
+second :=
+first: foo
+foo:
+	@echo foo
+second: bar
+bar:
+	@echo bar
+```
+
+Depending on Kati version converting it to Ninja file will yield either:
+
+```
+$ cat /tmp/1.ninja
+# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build first: phony foo
+rule rule0
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: rule0
+build second: phony bar
+rule rule1
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: rule1
+
+default first
+```
+
+or
+
+```
+$ cat 2.ninja
+# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build second: phony bar
+rule rule0
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: rule0
+build first: phony foo
+rule rule1
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: rule1
+
+default first
+```
+
+This is a quirk in Kati, see https://github.com/google/kati/issues/238
+
+Trying to find out the difference between the targets even after sorting them isn't too helpful:
+
+```
+diff <(grep '^build' /tmp/1.ninja|sort) <(grep '^build' /tmp/2.ninja | sort)
+1c1
+< build bar: rule1
+---
+> build bar: rule0
+3c3
+< build foo: rule0
+---
+> build foo: rule1
+```
+
+However, running these files through `canoninja` yields
+
+```
+$ canoninja /tmp/1.ninja
+# Generated by kati 06f2569b2d16628608c000a76e3d495a5a5528cb
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build first: phony foo
+rule R2f9981d3c152fc255370dc67028244f7bed72a03
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
+build second: phony bar
+rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+
+default first
+```
+
+and
+
+```
+~/go/bin/canoninja /tmp/2.ninja
+# Generated by kati 371194da71b3e191fea6f2ccceb7b061bd0de310
+
+pool local_pool
+ depth = 72
+
+build _kati_always_build_: phony
+
+build second: phony bar
+rule R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+ description = build $out
+ command = /bin/sh -c "echo bar"
+build bar: R62640f3f9095cf2da5b9d9e2a82f746cc710c94c
+build first: phony foo
+rule R2f9981d3c152fc255370dc67028244f7bed72a03
+ description = build $out
+ command = /bin/sh -c "echo foo"
+build foo: R2f9981d3c152fc255370dc67028244f7bed72a03
+
+default first
+```
+
+and when we extract only build statements and sort them, we see that both Ninja files define the same graph:
+
+```shell
+$ diff <(~/go/bin/canoninja /tmp/1.ninja | grep '^build' | sort) \
+       <(~/go/bin/canoninja /tmp/2.ninja | grep '^build' | sort)
+```
+
+# Todo
+
+* Optionally output only the build statements, optionally sorted
+* Handle continuation lines correctly
diff --git a/tools/canoninja/canoninja.go b/tools/canoninja/canoninja.go
new file mode 100644
index 0000000..681a694
--- /dev/null
+++ b/tools/canoninja/canoninja.go
@@ -0,0 +1,130 @@
+package canoninja
+
+import (
+	"bytes"
+	"crypto/sha1"
+	"encoding/hex"
+	"fmt"
+	"io"
+)
+
+var (
+	rulePrefix  = []byte("rule ")
+	buildPrefix = []byte("build ")
+	phonyRule   = []byte("phony")
+)
+
+func Generate(path string, buffer []byte, sink io.Writer) error {
+	// Break file into lines
+	from := 0
+	var lines [][]byte
+	for from < len(buffer) {
+		line := getLine(buffer[from:])
+		lines = append(lines, line)
+		from += len(line)
+	}
+
+	// FOr each rule, calculate and remember its digest
+	ruleDigest := make(map[string]string)
+	for i := 0; i < len(lines); {
+		if bytes.HasPrefix(lines[i], rulePrefix) {
+			// Find ruleName
+			rn := ruleName(lines[i])
+			if len(rn) == 0 {
+				return fmt.Errorf("%s:%d: rule name is missing or on the next line", path, i+1)
+			}
+			sRuleName := string(rn)
+			if _, ok := ruleDigest[sRuleName]; ok {
+				return fmt.Errorf("%s:%d: the rule %s has been already defined", path, i+1, sRuleName)
+			}
+			// Calculate rule text digest as a digests of line digests.
+			var digests []byte
+			doDigest := func(b []byte) {
+				h := sha1.New()
+				h.Write(b)
+				digests = h.Sum(digests)
+
+			}
+			// For the first line, digest everything after rule's name
+			doDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):])
+			for i++; i < len(lines) && lines[i][0] == ' '; i++ {
+				doDigest(lines[i])
+			}
+			h := sha1.New()
+			h.Write(digests)
+			ruleDigest[sRuleName] = "R" + hex.EncodeToString(h.Sum(nil))
+
+		} else {
+			i++
+		}
+	}
+
+	// Rewrite rule names.
+	for i, line := range lines {
+		if bytes.HasPrefix(line, buildPrefix) {
+			brn := getBuildRuleName(line)
+			if bytes.Equal(brn, phonyRule) {
+				sink.Write(line)
+				continue
+			}
+			if len(brn) == 0 {
+				return fmt.Errorf("%s:%d: build statement lacks rule name", path, i+1)
+			}
+			sink.Write(line[0 : cap(line)-cap(brn)])
+			if digest, ok := ruleDigest[string(brn)]; ok {
+				sink.Write([]byte(digest))
+			} else {
+				return fmt.Errorf("%s:%d: no rule for this build target", path, i+1)
+			}
+			sink.Write(line[cap(line)+len(brn)-cap(brn):])
+		} else if bytes.HasPrefix(line, rulePrefix) {
+			rn := ruleName(line)
+			// Write everything before it
+			sink.Write(line[0 : cap(line)-cap(rn)])
+			sink.Write([]byte(ruleDigest[string(rn)]))
+			sink.Write(line[cap(line)+len(rn)-cap(rn):])
+		} else {
+			//goland:noinspection GoUnhandledErrorResult
+			sink.Write(line)
+		}
+	}
+	return nil
+}
+
+func getLine(b []byte) []byte {
+	if n := bytes.IndexByte(b, '\n'); n >= 0 {
+		return b[:n+1]
+	}
+	return b
+}
+
+// Returns build statement's rule name
+func getBuildRuleName(line []byte) []byte {
+	n := bytes.IndexByte(line, ':')
+	if n <= 0 {
+		return nil
+	}
+	ruleName := line[n+1:]
+	if ruleName[0] == ' ' {
+		ruleName = bytes.TrimLeft(ruleName, " ")
+	}
+	if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
+		ruleName = ruleName[0:n]
+	}
+	return ruleName
+}
+
+// Returns rule statement's rule name
+func ruleName(lineAfterRule []byte) []byte {
+	ruleName := lineAfterRule[len(rulePrefix):]
+	if len(ruleName) == 0 {
+		return ruleName
+	}
+	if ruleName[0] == ' ' {
+		ruleName = bytes.TrimLeft(ruleName, " ")
+	}
+	if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
+		ruleName = ruleName[0:n]
+	}
+	return ruleName
+}
diff --git a/tools/canoninja/canoninja_test.go b/tools/canoninja/canoninja_test.go
new file mode 100644
index 0000000..3c45f8c
--- /dev/null
+++ b/tools/canoninja/canoninja_test.go
@@ -0,0 +1,47 @@
+package canoninja
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestGenerate(t *testing.T) {
+	tests := []struct {
+		name     string
+		in       []byte
+		wantSink string
+		wantErr  bool
+	}{
+		{
+			name: "1",
+			in: []byte(`
+rule rule1
+  abcd
+rule rule2
+  abcd
+build x: rule1
+`),
+			wantSink: `
+rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+  abcd
+rule R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+  abcd
+build x: R9c97aba7f61994be6862f5ea9a62d26130c7f48b
+`,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			sink := &bytes.Buffer{}
+			err := Generate("<file>", tt.in, sink)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if gotSink := sink.String(); gotSink != tt.wantSink {
+				t.Errorf("Generate() gotSink = %v, want %v", gotSink, tt.wantSink)
+			}
+		})
+	}
+}
diff --git a/tools/canoninja/cmd/canoninja.go b/tools/canoninja/cmd/canoninja.go
new file mode 100644
index 0000000..71802ef
--- /dev/null
+++ b/tools/canoninja/cmd/canoninja.go
@@ -0,0 +1,36 @@
+package main
+
+/*
+   Canoninja reads a Ninja file and changes the rule names to be the digest of the rule contents.
+   Feed  it to a filter that extracts only build statements, sort them, and you will have a crude
+   but effective tool to find small differences between two Ninja files.
+*/
+
+import (
+	"canoninja"
+	"flag"
+	"fmt"
+	"os"
+)
+
+func main() {
+	flag.Parse()
+	files := flag.Args()
+	if len(files) == 0 {
+		files = []string{"/dev/stdin"}
+	}
+	rc := 0
+	for _, f := range files {
+		if buffer, err := os.ReadFile(f); err == nil {
+			err = canoninja.Generate(f, buffer, os.Stdout)
+			if err != nil {
+				fmt.Fprintln(os.Stderr, err)
+				rc = 1
+			}
+		} else {
+			fmt.Fprintf(os.Stderr, "%s: %s\n", f, err)
+			rc = 1
+		}
+	}
+	os.Exit(rc)
+}
diff --git a/tools/canoninja/go.mod b/tools/canoninja/go.mod
new file mode 100644
index 0000000..c5a924e
--- /dev/null
+++ b/tools/canoninja/go.mod
@@ -0,0 +1 @@
+module canoninja
diff --git a/tools/check_elf_file.py b/tools/check_elf_file.py
index 1ff8e65..045cb1d 100755
--- a/tools/check_elf_file.py
+++ b/tools/check_elf_file.py
@@ -195,10 +195,12 @@
   @classmethod
   def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
     """Run llvm-readobj and parse the output."""
-    proc = subprocess.Popen(
-      [llvm_readobj, '-dynamic-table', '-dyn-symbols', elf_file_path],
-      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    cmd = [llvm_readobj, '--dynamic-table', '--dyn-symbols', elf_file_path]
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     out, _ = proc.communicate()
+    rc = proc.returncode
+    if rc != 0:
+      raise subprocess.CalledProcessError(rc, cmd, out)
     lines = out.splitlines()
     return cls._parse_llvm_readobj(elf_file_path, header, lines)
 
diff --git a/tools/event_log_tags.bzl b/tools/event_log_tags.bzl
new file mode 100644
index 0000000..3766da4
--- /dev/null
+++ b/tools/event_log_tags.bzl
@@ -0,0 +1,47 @@
+"""Event log tags generation rule"""
+
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@rules_android//rules:rules.bzl", "android_library")
+
+def _event_log_tags_impl(ctx):
+    out_files = []
+    for logtag_file in ctx.files.srcs:
+        out_filename = paths.replace_extension(logtag_file.basename, ".java")
+        out_file = ctx.actions.declare_file(out_filename)
+        out_files.append(out_file)
+        ctx.actions.run(
+            inputs = [logtag_file],
+            outputs = [out_file],
+            arguments = [
+                "-o",
+                out_file.path,
+                logtag_file.path,
+            ],
+            progress_message = "Generating Java logtag file from %s" % logtag_file.short_path,
+            executable = ctx.executable._logtag_to_java_tool,
+        )
+    return [DefaultInfo(files = depset(out_files))]
+
+_event_log_tags = rule(
+    implementation = _event_log_tags_impl,
+    attrs = {
+        "srcs": attr.label_list(allow_files = [".logtags"], mandatory = True),
+        "_logtag_to_java_tool": attr.label(
+            executable = True,
+            cfg = "exec",
+            allow_files = True,
+            default = Label("//build/make/tools:java-event-log-tags"),
+        ),
+    },
+)
+
+def event_log_tags(name, srcs):
+    _event_log_tags(
+        name = name + "_gen_logtags",
+        srcs = srcs,
+    )
+
+    android_library(
+        name = name,
+        srcs = [name + "_gen_logtags"],
+    )
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index 10d25e0..63cb4eb 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -27,7 +27,22 @@
 system_android_filesystem_config := system/core/libcutils/include/private/android_filesystem_config.h
 system_capability_header := bionic/libc/kernel/uapi/linux/capability.h
 
-# List of supported vendor, oem, odm, vendor_dlkm, odm_dlkm, product and system_ext Partitions
+# Use snapshots if exist
+vendor_android_filesystem_config := $(strip \
+  $(if $(filter-out current,$(BOARD_VNDK_VERSION)), \
+    $(SOONG_VENDOR_$(BOARD_VNDK_VERSION)_SNAPSHOT_DIR)/include/$(system_android_filesystem_config)))
+ifeq (,$(wildcard $(vendor_android_filesystem_config)))
+vendor_android_filesystem_config := $(system_android_filesystem_config)
+endif
+
+vendor_capability_header := $(strip \
+  $(if $(filter-out current,$(BOARD_VNDK_VERSION)), \
+    $(SOONG_VENDOR_$(BOARD_VNDK_VERSION)_SNAPSHOT_DIR)/include/$(system_capability_header)))
+ifeq (,$(wildcard $(vendor_capability_header)))
+vendor_capability_header := $(system_capability_header)
+endif
+
+# List of supported vendor, oem, odm, vendor_dlkm and odm_dlkm Partitions
 fs_config_generate_extra_partition_list := $(strip \
   $(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \
   $(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \
@@ -206,10 +221,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -232,10 +247,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -316,10 +331,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -342,10 +357,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -371,10 +386,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -397,10 +412,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -426,10 +441,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
@@ -452,10 +467,10 @@
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(system_android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(system_capability_header)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(vendor_android_filesystem_config)
+$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(vendor_capability_header)
 $(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(system_android_filesystem_config) $(system_capability_header)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(vendor_android_filesystem_config) $(vendor_capability_header)
 	@mkdir -p $(dir $@)
 	$< fsconfig \
 	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
diff --git a/tools/generate-notice-files.py b/tools/generate-notice-files.py
index bf958fb..5e3010f 100755
--- a/tools/generate-notice-files.py
+++ b/tools/generate-notice-files.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2012 The Android Open Source Project
 #
@@ -30,20 +30,18 @@
 import os
 import os.path
 import re
+import struct
 import sys
 
 MD5_BLOCKSIZE = 1024 * 1024
 HTML_ESCAPE_TABLE = {
-    "&": "&amp;",
-    '"': "&quot;",
-    "'": "&apos;",
-    ">": "&gt;",
-    "<": "&lt;",
+    b"&": b"&amp;",
+    b'"': b"&quot;",
+    b"'": b"&apos;",
+    b">": b"&gt;",
+    b"<": b"&lt;",
     }
 
-def hexify(s):
-    return ("%02x"*len(s)) % tuple(map(ord, s))
-
 def md5sum(filename):
     """Calculate an MD5 of the file given by FILENAME,
     and return hex digest as a string.
@@ -57,20 +55,26 @@
             break
         sum.update(block)
     f.close()
-    return hexify(sum.digest())
+    return sum.hexdigest()
 
 
 def html_escape(text):
     """Produce entities within text."""
-    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+    # Using for i in text doesn't work since i will be an int, not a byte.
+    # There are multiple ways to solve this, but the most performant way
+    # to iterate over a byte array is to use unpack. Using the
+    # for i in range(len(text)) and using that to get a byte using array
+    # slices is twice as slow as this method.
+    return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
 
-HTML_OUTPUT_CSS="""
+HTML_OUTPUT_CSS=b"""
 <style type="text/css">
 body { padding: 0; font-family: sans-serif; }
 .same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
 .label { font-weight: bold; }
 .file-list { margin-left: 1em; color: blue; }
 </style>
+
 """
 
 def combine_notice_files_html(file_hash, input_dirs, output_filename):
@@ -90,13 +94,13 @@
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, "<html><head>"
-    print >> output_file, HTML_OUTPUT_CSS
-    print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
+    output_file.write(b"<html><head>\n")
+    output_file.write(HTML_OUTPUT_CSS)
+    output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
 
     # Output our table of contents
-    print >> output_file, '<div class="toc">'
-    print >> output_file, "<ul>"
+    output_file.write(b'<div class="toc">\n')
+    output_file.write(b"<ul>\n")
 
     # Flatten the list of lists into a single list of filenames
     sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
@@ -104,31 +108,29 @@
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
+        output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
 
-    print >> output_file, "</ul>"
-    print >> output_file, "</div><!-- table of contents -->"
+    output_file.write(b"</ul>\n")
+    output_file.write(b"</div><!-- table of contents -->\n")
     # Output the individual notice file lists
-    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+    output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
     for value in file_hash:
-        print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
-        print >> output_file, '<div class="label">Notices for file(s):</div>'
-        print >> output_file, '<div class="file-list">'
+        output_file.write(b'<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0]))
+        output_file.write(b'<div class="label">Notices for file(s):</div>\n')
+        output_file.write(b'<div class="file-list">\n')
         for filename in value:
-            print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
-        print >> output_file, "</div><!-- file-list -->"
-        print >> output_file
-        print >> output_file, '<pre class="license-text">'
-        print >> output_file, html_escape(open(value[0]).read())
-        print >> output_file, "</pre><!-- license-text -->"
-        print >> output_file, "</td></tr><!-- same-license -->"
-        print >> output_file
-        print >> output_file
-        print >> output_file
+            output_file.write(("%s <br/>\n" % SRC_DIR_STRIP_RE.sub(r"\1", filename)).encode())
+        output_file.write(b"</div><!-- file-list -->\n")
+        output_file.write(b"\n")
+        output_file.write(b'<pre class="license-text">\n')
+        with open(value[0], "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"\n</pre><!-- license-text -->\n")
+        output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
 
     # Finish off the file output
-    print >> output_file, "</table>"
-    print >> output_file, "</body></html>"
+    output_file.write(b"</table>\n")
+    output_file.write(b"</body></html>\n")
     output_file.close()
 
 def combine_notice_files_text(file_hash, input_dirs, output_filename, file_title):
@@ -136,14 +138,18 @@
 
     SRC_DIR_STRIP_RE = re.compile("(?:" + "|".join(input_dirs) + ")(/.*).txt")
     output_file = open(output_filename, "wb")
-    print >> output_file, file_title
+    output_file.write(file_title.encode())
+    output_file.write(b"\n")
     for value in file_hash:
-      print >> output_file, "============================================================"
-      print >> output_file, "Notices for file(s):"
-      for filename in value:
-        print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
-      print >> output_file, "------------------------------------------------------------"
-      print >> output_file, open(value[0]).read()
+        output_file.write(b"============================================================\n")
+        output_file.write(b"Notices for file(s):\n")
+        for filename in value:
+            output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
+            output_file.write(b"\n")
+        output_file.write(b"------------------------------------------------------------\n")
+        with open(value[0], "rb") as notice_file:
+            output_file.write(notice_file.read())
+            output_file.write(b"\n")
     output_file.close()
 
 def combine_notice_files_xml(files_with_same_hash, input_dirs, output_filename):
@@ -154,15 +160,15 @@
     # Set up a filename to row id table (anchors inside tables don't work in
     # most browsers, but href's to table row ids do)
     id_table = {}
-    for file_key in files_with_same_hash.keys():
-        for filename in files_with_same_hash[file_key]:
+    for file_key, files in files_with_same_hash.items():
+        for filename in files:
              id_table[filename] = file_key
 
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, '<?xml version="1.0" encoding="utf-8"?>'
-    print >> output_file, "<licenses>"
+    output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
+    output_file.write(b"<licenses>\n")
 
     # Flatten the list of lists into a single list of filenames
     sorted_filenames = sorted(id_table.keys())
@@ -170,10 +176,8 @@
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<file-name contentId="%s">%s</file-name>' % (id_table.get(filename), stripped_filename)
-
-    print >> output_file
-    print >> output_file
+        output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
+    output_file.write(b"\n\n")
 
     processed_file_keys = []
     # Output the individual notice file lists
@@ -183,11 +187,13 @@
             continue
         processed_file_keys.append(file_key)
 
-        print >> output_file, '<file-content contentId="%s"><![CDATA[%s]]></file-content>' % (file_key, html_escape(open(filename).read()))
-        print >> output_file
+        output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
+        with open(filename, "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"]]></file-content>\n\n")
 
     # Finish off the file output
-    print >> output_file, "</licenses>"
+    output_file.write(b"</licenses>\n")
     output_file.close()
 
 def get_args():
@@ -254,7 +260,7 @@
                     file_md5sum = md5sum(filename)
                     files_with_same_hash[file_md5sum].append(filename)
 
-    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(files_with_same_hash.keys())]
+    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
     combine_notice_files_text(filesets, input_dirs, txt_output_file, file_title)
 
     if html_output_file is not None:
diff --git a/tools/rbcrun/README.md b/tools/rbcrun/README.md
index fb58c89..7f40597 100644
--- a/tools/rbcrun/README.md
+++ b/tools/rbcrun/README.md
@@ -68,6 +68,11 @@
 
 Returns `True`  if *file* exists
 
+#### rblf_find_files(*top*, *file-pattern*, only_files = 0)
+
+Returns all the paths under *top* whose basename matches *pattern* (which is a shell's glob pattern). If *only_files* is
+not zero, only the paths to the regular files are returned. The returned paths are relative to *top*.
+
 #### rblf_wildcard(*glob*, *top* = None)
 
 Expands *glob*. If *top* is supplied, expands "*top*/*glob*", then removes
@@ -82,3 +87,7 @@
 Runs `sh -c "`*command*`"`, reads its output, converts all newlines into spaces, chops trailing newline returns this
 string. This is equivalent to Make's
 `shell` builtin function. *This function will be eventually removed*.
+
+#### rblf_log(*arg*,..., sep=' ')
+
+Same as `print` builtin but writes to stderr.
\ No newline at end of file
diff --git a/tools/rbcrun/cmd/rbcrun.go b/tools/rbcrun/cmd/rbcrun.go
index 7848562..4db6a0b 100644
--- a/tools/rbcrun/cmd/rbcrun.go
+++ b/tools/rbcrun/cmd/rbcrun.go
@@ -93,6 +93,6 @@
 }
 
 func quit(format string, s ...interface{}) {
-	fmt.Fprintln(os.Stderr, format, s)
+	fmt.Fprintf(os.Stderr, format, s...)
 	os.Exit(2)
 }
diff --git a/tools/rbcrun/host.go b/tools/rbcrun/host.go
index 1e43334..4915de9 100644
--- a/tools/rbcrun/host.go
+++ b/tools/rbcrun/host.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"io/fs"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -118,7 +119,7 @@
 	if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &path); err != nil {
 		return starlark.None, err
 	}
-	if stat, err := os.Stat(path); err != nil || stat.IsDir() {
+	if _, err := os.Stat(path); err != nil {
 		return starlark.False, nil
 	}
 	return starlark.True, nil
@@ -170,6 +171,46 @@
 	return makeStringList(files), nil
 }
 
+// find(top, pattern, only_files = 0) returns all the paths under 'top'
+// whose basename matches 'pattern' (which is a shell's glob pattern).
+// If 'only_files' is non-zero, only the paths to the regular files are
+// returned. The returned paths are relative to 'top'.
+func find(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple,
+	kwargs []starlark.Tuple) (starlark.Value, error) {
+	var top, pattern string
+	var onlyFiles int
+	if err := starlark.UnpackArgs(b.Name(), args, kwargs,
+		"top", &top, "pattern", &pattern, "only_files?", &onlyFiles); err != nil {
+		return starlark.None, err
+	}
+	top = filepath.Clean(top)
+	pattern = filepath.Clean(pattern)
+	// Go's filepath.Walk is slow, consider using OS's find
+	var res []string
+	err := filepath.WalkDir(top, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			if d != nil && d.IsDir() {
+				return fs.SkipDir
+			} else {
+				return nil
+			}
+		}
+		relPath := strings.TrimPrefix(path, top)
+		if len(relPath) > 0 && relPath[0] == os.PathSeparator {
+			relPath = relPath[1:]
+		}
+		// Do not return top-level dir
+		if len(relPath) == 0 {
+			return nil
+		}
+		if matched, err := filepath.Match(pattern, d.Name()); err == nil && matched && (onlyFiles == 0 || d.Type().IsRegular()) {
+			res = append(res, relPath)
+		}
+		return nil
+	})
+	return makeStringList(res), err
+}
+
 // shell(command) runs OS shell with given command and returns back
 // its output the same way as Make's $(shell ) function. The end-of-lines
 // ("\n" or "\r\n") are replaced with " " in the result, and the trailing
@@ -218,6 +259,28 @@
 	return starlarkstruct.FromStringDict(starlarkstruct.Default, sd)
 }
 
+func log(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+	sep := " "
+	if err := starlark.UnpackArgs("print", nil, kwargs, "sep?", &sep); err != nil {
+		return nil, err
+	}
+	for i, v := range args {
+		if i > 0 {
+			fmt.Fprint(os.Stderr, sep)
+		}
+		if s, ok := starlark.AsString(v); ok {
+			fmt.Fprint(os.Stderr, s)
+		} else if b, ok := v.(starlark.Bytes); ok {
+			fmt.Fprint(os.Stderr, string(b))
+		} else {
+			fmt.Fprintf(os.Stderr, "%s", v)
+		}
+	}
+
+	fmt.Fprintln(os.Stderr)
+	return starlark.None, nil
+}
+
 func setup(env []string) {
 	// Create the symbols that aid makefile conversion. See README.md
 	builtins = starlark.StringDict{
@@ -226,10 +289,14 @@
 		"rblf_env": structFromEnv(os.Environ()),
 		// To convert makefile's $(wildcard foo)
 		"rblf_file_exists": starlark.NewBuiltin("rblf_file_exists", fileExists),
+		// To convert find-copy-subdir and product-copy-files-by pattern
+		"rblf_find_files": starlark.NewBuiltin("rblf_find_files", find),
 		// To convert makefile's $(filter ...)/$(filter-out)
 		"rblf_regex": starlark.NewBuiltin("rblf_regex", regexMatch),
 		// To convert makefile's $(shell cmd)
 		"rblf_shell": starlark.NewBuiltin("rblf_shell", shell),
+		// Output to stderr
+		"rblf_log": starlark.NewBuiltin("rblf_log", log),
 		// To convert makefile's $(wildcard foo*)
 		"rblf_wildcard": starlark.NewBuiltin("rblf_wildcard", wildcard),
 	}
diff --git a/tools/rbcrun/testdata/file_ops.star b/tools/rbcrun/testdata/file_ops.star
index e1f1ac2..50e39bf 100644
--- a/tools/rbcrun/testdata/file_ops.star
+++ b/tools/rbcrun/testdata/file_ops.star
@@ -4,15 +4,22 @@
 
 def test():
     myname = "file_ops.star"
+    assert.true(rblf_file_exists("."), "./ exists ")
     assert.true(rblf_file_exists(myname), "the file %s does exist" % myname)
     assert.true(not rblf_file_exists("no_such_file"), "the file no_such_file does not exist")
     files = rblf_wildcard("*.star")
     assert.true(myname in files, "expected %s in  %s" % (myname, files))
-    # RBCDATADIR is set by the caller to the path where this file resides
     files = rblf_wildcard("*.star", rblf_env.TEST_DATA_DIR)
     assert.true(myname in files, "expected %s in %s" % (myname, files))
     files = rblf_wildcard("*.xxx")
     assert.true(len(files) == 0, "expansion should be empty but contains %s" % files)
-
-
+    mydir = "testdata"
+    myrelname = "%s/%s" % (mydir, myname)
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*")
+    assert.true(mydir in files and myrelname in files, "expected %s and %s in %s" % (mydir, myrelname, files))
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*", only_files=1)
+    assert.true(mydir not in files, "did not expect %s in %s" % (mydir, files))
+    assert.true(myrelname in files, "expected %s  in %s" % (myrelname, files))
+    files = rblf_find_files(rblf_env.TEST_DATA_DIR + "/../", "*.star")
+    assert.true(myrelname in files, "expected %s in %s" % (myrelname, files))
 test()
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 5ee53c8..fc588e4 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -164,6 +164,7 @@
         "releasetools_common",
         "releasetools_verity_utils",
         "apex_manifest",
+        "care_map_proto_py",
     ],
     required: [
         "brillo_update_payload",
@@ -400,7 +401,7 @@
         "releasetools_common",
     ],
     required: [
-        "aapt",
+        "aapt2",
     ],
 }
 
diff --git a/tools/releasetools/OWNERS b/tools/releasetools/OWNERS
index d7fc540..9962836 100644
--- a/tools/releasetools/OWNERS
+++ b/tools/releasetools/OWNERS
@@ -1,4 +1,6 @@
 elsk@google.com
 nhdo@google.com
 xunchang@google.com
-zhaojiac@google.com
+
+per-file merge_*.py = danielnorman@google.com
+
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index babfc7d..2a4b56b 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -589,7 +589,7 @@
     AssertionError: If it can't find an image.
   """
   for partition in ab_partitions:
-    img_name = partition.strip() + ".img"
+    img_name = partition + ".img"
 
     # Assert that the image is present under IMAGES/ now.
     if output_zip:
@@ -687,8 +687,10 @@
               os.path.join(OPTIONS.input_tmp, "IMAGES",
                            "{}.img".format(partition_name))))
 
+
 def AddApexInfo(output_zip):
-  apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system')
+  apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
+                                          compressed_only=False)
   apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
   apex_metadata_proto.apex_info.extend(apex_infos)
   apex_info_bytes = apex_metadata_proto.SerializeToString()
@@ -710,8 +712,10 @@
   # Calculate the vbmeta digest and put the result in to META/
   boot_images = OPTIONS.info_dict.get("boot_images")
   # Disable the digest calculation if the target_file is used as a container
-  # for boot images.
-  boot_container = boot_images and len(boot_images.split()) >= 2
+  # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
+  # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
+  boot_container = boot_images and (
+      len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
   if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
       OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
     avbtool = OPTIONS.info_dict["avb_avbtool"]
@@ -854,39 +858,23 @@
         if output_zip:
           recovery_two_step_image.AddToZip(output_zip)
 
-  if has_system:
-    banner("system")
-    partitions['system'] = AddSystem(
-        output_zip, recovery_img=recovery_image, boot_img=boot_image)
+  def add_partition(partition, has_partition, add_func, add_args):
+    if has_partition:
+      banner(partition)
+      partitions[partition] = add_func(output_zip, *add_args)
 
-  if has_vendor:
-    banner("vendor")
-    partitions['vendor'] = AddVendor(
-        output_zip, recovery_img=recovery_image, boot_img=boot_image)
-
-  if has_product:
-    banner("product")
-    partitions['product'] = AddProduct(output_zip)
-
-  if has_system_ext:
-    banner("system_ext")
-    partitions['system_ext'] = AddSystemExt(output_zip)
-
-  if has_odm:
-    banner("odm")
-    partitions['odm'] = AddOdm(output_zip)
-
-  if has_vendor_dlkm:
-    banner("vendor_dlkm")
-    partitions['vendor_dlkm'] = AddVendorDlkm(output_zip)
-
-  if has_odm_dlkm:
-    banner("odm_dlkm")
-    partitions['odm_dlkm'] = AddOdmDlkm(output_zip)
-
-  if has_system_other:
-    banner("system_other")
-    AddSystemOther(output_zip)
+  add_partition_calls = (
+      ("system", has_system, AddSystem, [recovery_image, boot_image]),
+      ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
+      ("product", has_product, AddProduct, []),
+      ("system_ext", has_system_ext, AddSystemExt, []),
+      ("odm", has_odm, AddOdm, []),
+      ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
+      ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
+      ("system_other", has_system_other, AddSystemOther, []),
+  )
+  for call in add_partition_calls:
+    add_partition(*call)
 
   AddApexInfo(output_zip)
 
@@ -900,13 +888,10 @@
     banner("partition-table")
     AddPartitionTable(output_zip)
 
-  if OPTIONS.info_dict.get("has_dtbo") == "true":
-    banner("dtbo")
-    partitions['dtbo'] = AddDtbo(output_zip)
-
-  if OPTIONS.info_dict.get("has_pvmfw") == "true":
-    banner("pvmfw")
-    partitions['pvmfw'] = AddPvmfw(output_zip)
+  add_partition("dtbo",
+                OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
+  add_partition("pvmfw",
+                OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
 
   # Custom images.
   custom_partitions = OPTIONS.info_dict.get(
@@ -963,7 +948,7 @@
                                    "ab_partitions.txt")
   if os.path.exists(ab_partitions_txt):
     with open(ab_partitions_txt) as f:
-      ab_partitions = f.readlines()
+      ab_partitions = f.read().splitlines()
 
     # For devices using A/B update, make sure we have all the needed images
     # ready under IMAGES/ or RADIO/.
@@ -1028,8 +1013,5 @@
   try:
     common.CloseInheritedPipes()
     main(sys.argv[1:])
-  except common.ExternalError:
-    logger.exception("\n   ERROR:\n")
-    sys.exit(1)
   finally:
     common.Cleanup()
diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py
index ef4c69c..ee0feae 100644
--- a/tools/releasetools/apex_utils.py
+++ b/tools/releasetools/apex_utils.py
@@ -52,9 +52,9 @@
 
 
 class ApexApkSigner(object):
-  """Class to sign the apk files in a apex payload image and repack the apex"""
+  """Class to sign the apk files and other files in an apex payload image and repack the apex"""
 
-  def __init__(self, apex_path, key_passwords, codename_to_api_level_map):
+  def __init__(self, apex_path, key_passwords, codename_to_api_level_map, avbtool=None, sign_tool=None):
     self.apex_path = apex_path
     if not key_passwords:
       self.key_passwords = dict()
@@ -63,9 +63,11 @@
     self.codename_to_api_level_map = codename_to_api_level_map
     self.debugfs_path = os.path.join(
         OPTIONS.search_path, "bin", "debugfs_static")
+    self.avbtool = avbtool if avbtool else "avbtool"
+    self.sign_tool = sign_tool
 
   def ProcessApexFile(self, apk_keys, payload_key, signing_args=None):
-    """Scans and signs the apk files and repack the apex
+    """Scans and signs the payload files and repack the apex
 
     Args:
       apk_keys: A dict that holds the signing keys for apk files.
@@ -84,7 +86,7 @@
     apk_entries = [name for name in entries_names if name.endswith('.apk')]
 
     # No need to sign and repack, return the original apex path.
-    if not apk_entries:
+    if not apk_entries and self.sign_tool is None:
       logger.info('No apk file to sign in %s', self.apex_path)
       return self.apex_path
 
@@ -99,15 +101,15 @@
         logger.warning('Apk path does not contain the intended directory name:'
                        ' %s', entry)
 
-    payload_dir, has_signed_apk = self.ExtractApexPayloadAndSignApks(
-        apk_entries, apk_keys)
-    if not has_signed_apk:
-      logger.info('No apk file has been signed in %s', self.apex_path)
+    payload_dir, has_signed_content = self.ExtractApexPayloadAndSignContents(
+        apk_entries, apk_keys, payload_key)
+    if not has_signed_content:
+      logger.info('No contents has been signed in %s', self.apex_path)
       return self.apex_path
 
     return self.RepackApexPayload(payload_dir, payload_key, signing_args)
 
-  def ExtractApexPayloadAndSignApks(self, apk_entries, apk_keys):
+  def ExtractApexPayloadAndSignContents(self, apk_entries, apk_keys, payload_key):
     """Extracts the payload image and signs the containing apk files."""
     if not os.path.exists(self.debugfs_path):
       raise ApexSigningError(
@@ -119,7 +121,7 @@
                    self.debugfs_path, 'extract', self.apex_path, payload_dir]
     common.RunAndCheckOutput(extract_cmd)
 
-    has_signed_apk = False
+    has_signed_content = False
     for entry in apk_entries:
       apk_path = os.path.join(payload_dir, entry)
       assert os.path.exists(self.apex_path)
@@ -137,8 +139,15 @@
       common.SignFile(
           unsigned_apk, apk_path, key_name, self.key_passwords.get(key_name),
           codename_to_api_level_map=self.codename_to_api_level_map)
-      has_signed_apk = True
-    return payload_dir, has_signed_apk
+      has_signed_content = True
+
+    if self.sign_tool:
+      logger.info('Signing payload contents in apex %s with %s', self.apex_path, self.sign_tool)
+      cmd = [self.sign_tool, '--avbtool', self.avbtool, payload_key, payload_dir]
+      common.RunAndCheckOutput(cmd)
+      has_signed_content = True
+
+    return payload_dir, has_signed_content
 
   def RepackApexPayload(self, payload_dir, payload_key, signing_args=None):
     """Rebuilds the apex file with the updated payload directory."""
@@ -310,7 +319,7 @@
 
 def SignUncompressedApex(avbtool, apex_file, payload_key, container_key,
                          container_pw, apk_keys, codename_to_api_level_map,
-                         no_hashtree, signing_args=None):
+                         no_hashtree, signing_args=None, sign_tool=None):
   """Signs the current uncompressed APEX with the given payload/container keys.
 
   Args:
@@ -322,14 +331,16 @@
     codename_to_api_level_map: A dict that maps from codename to API level.
     no_hashtree: Don't include hashtree in the signed APEX.
     signing_args: Additional args to be passed to the payload signer.
+    sign_tool: A tool to sign the contents of the APEX.
 
   Returns:
     The path to the signed APEX file.
   """
-  # 1. Extract the apex payload image and sign the containing apk files. Repack
+  # 1. Extract the apex payload image and sign the files (e.g. APKs). Repack
   # the apex file after signing.
   apk_signer = ApexApkSigner(apex_file, container_pw,
-                             codename_to_api_level_map)
+                             codename_to_api_level_map,
+                             avbtool, sign_tool)
   apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)
 
   # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
@@ -363,20 +374,16 @@
   common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
   common.ZipClose(apex_zip)
 
-  # 3. 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])
-
-  # 4. Sign the APEX container with container_key.
+  # 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'])
+  extra_signapk_args.extend(['-a', '4096', '--align-file-size'])
 
   password = container_pw.get(container_key) if container_pw else None
   common.SignFile(
-      aligned_apex,
+      apex_file,
       signed_apex,
       container_key,
       password,
@@ -388,7 +395,7 @@
 
 def SignCompressedApex(avbtool, apex_file, payload_key, container_key,
                        container_pw, apk_keys, codename_to_api_level_map,
-                       no_hashtree, signing_args=None):
+                       no_hashtree, signing_args=None, sign_tool=None):
   """Signs the current compressed APEX with the given payload/container keys.
 
   Args:
@@ -425,7 +432,8 @@
       apk_keys,
       codename_to_api_level_map,
       no_hashtree,
-      signing_args)
+      signing_args,
+      sign_tool)
 
   # 3. Compress signed original apex.
   compressed_apex_file = common.MakeTempFile(prefix='apex-container-',
@@ -436,33 +444,24 @@
                             '--input', signed_original_apex_file,
                             '--output', compressed_apex_file])
 
-  # 4. Align apex
-  aligned_apex = common.MakeTempFile(prefix='apex-container-', suffix='.capex')
-  common.RunAndCheckOutput(['zipalign', '-f', '4096', compressed_apex_file,
-                            aligned_apex])
-
-  # 5. Sign the APEX container with container_key.
+  # 4. Sign the APEX container with container_key.
   signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.capex')
 
-  # Specify the 4K alignment when calling SignApk.
-  extra_signapk_args = OPTIONS.extra_signapk_args[:]
-  extra_signapk_args.extend(['-a', '4096'])
-
   password = container_pw.get(container_key) if container_pw else None
   common.SignFile(
-      aligned_apex,
+      compressed_apex_file,
       signed_apex,
       container_key,
       password,
       codename_to_api_level_map=codename_to_api_level_map,
-      extra_signapk_args=extra_signapk_args)
+      extra_signapk_args=OPTIONS.extra_signapk_args)
 
   return signed_apex
 
 
 def SignApex(avbtool, apex_data, payload_key, container_key, container_pw,
              apk_keys, codename_to_api_level_map,
-             no_hashtree, signing_args=None):
+             no_hashtree, signing_args=None, sign_tool=None):
   """Signs the current APEX with the given payload/container keys.
 
   Args:
@@ -498,7 +497,8 @@
           codename_to_api_level_map=codename_to_api_level_map,
           no_hashtree=no_hashtree,
           apk_keys=apk_keys,
-          signing_args=signing_args)
+          signing_args=signing_args,
+          sign_tool=sign_tool)
     elif apex_type == 'COMPRESSED':
       return SignCompressedApex(
           avbtool,
@@ -509,7 +509,8 @@
           codename_to_api_level_map=codename_to_api_level_map,
           no_hashtree=no_hashtree,
           apk_keys=apk_keys,
-          signing_args=signing_args)
+          signing_args=signing_args,
+          sign_tool=sign_tool)
     else:
       # TODO(b/172912232): support signing compressed apex
       raise ApexInfoError('Unsupported apex type {}'.format(apex_type))
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index fa4a152..38104af 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -231,6 +231,22 @@
             mount_point, total_blocks, used_blocks, headroom_blocks,
             adjusted_blocks))
 
+def CalculateSizeAndReserved(prop_dict, size):
+  fs_type = prop_dict.get("fs_type", "")
+  partition_headroom = int(prop_dict.get("partition_headroom", 0))
+  # If not specified, give us 16MB margin for GetDiskUsage error ...
+  reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
+
+  if fs_type == "erofs":
+    reserved_size = int(prop_dict.get("partition_reserved_size", 0))
+    if reserved_size == 0:
+      # give .3% margin or a minimum size for AVB footer
+      return max(size * 1003 // 1000, 256 * 1024)
+
+  if fs_type.startswith("ext4") and partition_headroom > reserved_size:
+    reserved_size = partition_headroom
+
+  return size + reserved_size
 
 def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):
   """Builds a pure image for the files under in_dir and writes it to out_file.
@@ -256,9 +272,11 @@
   needs_casefold = prop_dict.get("needs_casefold", 0)
   needs_compress = prop_dict.get("needs_compress", 0)
 
+  disable_sparse = "disable_sparse" in prop_dict
+
   if fs_type.startswith("ext"):
     build_command = [prop_dict["ext_mkuserimg"]]
-    if "extfs_sparse_flag" in prop_dict:
+    if "extfs_sparse_flag" in prop_dict and not disable_sparse:
       build_command.append(prop_dict["extfs_sparse_flag"])
       run_e2fsck = True
     build_command.extend([in_dir, out_file, fs_type,
@@ -287,7 +305,7 @@
     if "flash_logical_block_size" in prop_dict:
       build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
     # Specify UUID and hash_seed if using mke2fs.
-    if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":
+    if os.path.basename(prop_dict["ext_mkuserimg"]) == "mkuserimg_mke2fs":
       if "uuid" in prop_dict:
         build_command.extend(["-U", prop_dict["uuid"]])
       if "hash_seed" in prop_dict:
@@ -303,7 +321,7 @@
   elif fs_type.startswith("erofs"):
     build_command = ["mkerofsimage.sh"]
     build_command.extend([in_dir, out_file])
-    if "erofs_sparse_flag" in prop_dict:
+    if "erofs_sparse_flag" in prop_dict and not disable_sparse:
       build_command.extend([prop_dict["erofs_sparse_flag"]])
     build_command.extend(["-m", prop_dict["mount_point"]])
     if target_out:
@@ -312,14 +330,27 @@
       build_command.extend(["-C", fs_config])
     if "selinux_fc" in prop_dict:
       build_command.extend(["-c", prop_dict["selinux_fc"]])
+    compressor = None
+    if "erofs_default_compressor" in prop_dict:
+      compressor = prop_dict["erofs_default_compressor"]
+    if "erofs_compressor" in prop_dict:
+      compressor = prop_dict["erofs_compressor"]
+    if compressor:
+      build_command.extend(["-z", compressor])
     if "timestamp" in prop_dict:
       build_command.extend(["-T", str(prop_dict["timestamp"])])
     if "uuid" in prop_dict:
       build_command.extend(["-U", prop_dict["uuid"]])
+    if "block_list" in prop_dict:
+      build_command.extend(["-B", prop_dict["block_list"]])
+    if "erofs_pcluster_size" in prop_dict:
+      build_command.extend(["-P", prop_dict["erofs_pcluster_size"]])
+    if "erofs_share_dup_blocks" in prop_dict:
+      build_command.extend(["-k", "4096"])
   elif fs_type.startswith("squash"):
     build_command = ["mksquashfsimage.sh"]
     build_command.extend([in_dir, out_file])
-    if "squashfs_sparse_flag" in prop_dict:
+    if "squashfs_sparse_flag" in prop_dict and not disable_sparse:
       build_command.extend([prop_dict["squashfs_sparse_flag"]])
     build_command.extend(["-m", prop_dict["mount_point"]])
     if target_out:
@@ -341,7 +372,7 @@
   elif fs_type.startswith("f2fs"):
     build_command = ["mkf2fsuserimg.sh"]
     build_command.extend([out_file, prop_dict["image_size"]])
-    if "f2fs_sparse_flag" in prop_dict:
+    if "f2fs_sparse_flag" in prop_dict and not disable_sparse:
       build_command.extend([prop_dict["f2fs_sparse_flag"]])
     if fs_config:
       build_command.extend(["-C", fs_config])
@@ -353,6 +384,8 @@
     build_command.extend(["-t", prop_dict["mount_point"]])
     if "timestamp" in prop_dict:
       build_command.extend(["-T", str(prop_dict["timestamp"])])
+    if "block_list" in prop_dict:
+      build_command.extend(["-B", prop_dict["block_list"]])
     build_command.extend(["-L", prop_dict["mount_point"]])
     if (needs_projid):
       build_command.append("--prjquota")
@@ -360,8 +393,9 @@
       build_command.append("--casefold")
     if (needs_compress or prop_dict.get("f2fs_compress") == "true"):
       build_command.append("--compression")
-    if (prop_dict.get("f2fs_compress") == "true"):
+    if (prop_dict.get("mount_point") != "data"):
       build_command.append("--readonly")
+    if (prop_dict.get("f2fs_compress") == "true"):
       build_command.append("--sldc")
       if (prop_dict.get("f2fs_sldc_flags") == None):
         build_command.append(str(0))
@@ -445,27 +479,25 @@
   # or None if not applicable.
   verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
 
+  disable_sparse = "disable_sparse" in prop_dict
+  mkfs_output = None
   if (prop_dict.get("use_dynamic_partition_size") == "true" and
       "partition_size" not in prop_dict):
     # If partition_size is not defined, use output of `du' + reserved_size.
     # For compressed file system, it's better to use the compressed size to avoid wasting space.
     if fs_type.startswith("erofs"):
-      tmp_dict = prop_dict.copy()
-      if "erofs_sparse_flag" in tmp_dict:
-        tmp_dict.pop("erofs_sparse_flag")
-      BuildImageMkfs(in_dir, tmp_dict, out_file, target_out, fs_config)
-      size = GetDiskUsage(out_file)
-      os.remove(out_file)
+      mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
+      if "erofs_sparse_flag" in prop_dict and not disable_sparse:
+        image_path = UnsparseImage(out_file, replace=False)
+        size = GetDiskUsage(image_path)
+        os.remove(image_path)
+      else:
+        size = GetDiskUsage(out_file)
     else:
       size = GetDiskUsage(in_dir)
     logger.info(
         "The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
-    # If not specified, give us 16MB margin for GetDiskUsage error ...
-    reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
-    partition_headroom = int(prop_dict.get("partition_headroom", 0))
-    if fs_type.startswith("ext4") and partition_headroom > reserved_size:
-      reserved_size = partition_headroom
-    size += reserved_size
+    size = CalculateSizeAndReserved(prop_dict, size)
     # Round this up to a multiple of 4K so that avbtool works
     size = common.RoundUpTo4K(size)
     if fs_type.startswith("ext"):
@@ -478,7 +510,7 @@
           size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
       BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
       sparse_image = False
-      if "extfs_sparse_flag" in prop_dict:
+      if "extfs_sparse_flag" in prop_dict and not disable_sparse:
         sparse_image = True
       fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)
       os.remove(out_file)
@@ -522,7 +554,7 @@
       prop_dict["image_size"] = str(size)
       BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
       sparse_image = False
-      if "f2fs_sparse_flag" in prop_dict:
+      if "f2fs_sparse_flag" in prop_dict and not disable_sparse:
         sparse_image = True
       fs_dict = GetFilesystemCharacteristics(fs_type, out_file, sparse_image)
       os.remove(out_file)
@@ -543,7 +575,8 @@
     max_image_size = verity_image_builder.CalculateMaxImageSize()
     prop_dict["image_size"] = str(max_image_size)
 
-  mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
+  if not mkfs_output:
+    mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
 
   # Check if there's enough headroom space available for ext4 image.
   if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
@@ -587,6 +620,9 @@
 
   common_props = (
       "extfs_sparse_flag",
+      "erofs_default_compressor",
+      "erofs_pcluster_size",
+      "erofs_share_dup_blocks",
       "erofs_sparse_flag",
       "squashfs_sparse_flag",
       "system_f2fs_compress",
@@ -606,238 +642,96 @@
   for p in common_props:
     copy_prop(p, p)
 
+  ro_mount_points = set([
+      "odm",
+      "odm_dlkm",
+      "oem",
+      "product",
+      "system",
+      "system_ext",
+      "system_other",
+      "vendor",
+      "vendor_dlkm",
+  ])
+
+  # Tuple layout: (readonly, specific prop, general prop)
+  fmt_props = (
+      # Generic first, then specific file type.
+      (False, "fs_type", "fs_type"),
+      (False, "{}_fs_type", "fs_type"),
+
+      # Ordering for these doesn't matter.
+      (False, "{}_selinux_fc", "selinux_fc"),
+      (False, "{}_size", "partition_size"),
+      (True, "avb_{}_add_hashtree_footer_args", "avb_add_hashtree_footer_args"),
+      (True, "avb_{}_algorithm", "avb_algorithm"),
+      (True, "avb_{}_hashtree_enable", "avb_hashtree_enable"),
+      (True, "avb_{}_key_path", "avb_key_path"),
+      (True, "avb_{}_salt", "avb_salt"),
+      (True, "ext4_share_dup_blocks", "ext4_share_dup_blocks"),
+      (True, "{}_base_fs_file", "base_fs_file"),
+      (True, "{}_disable_sparse", "disable_sparse"),
+      (True, "{}_erofs_compressor", "erofs_compressor"),
+      (True, "{}_erofs_pcluster_size", "erofs_pcluster_size"),
+      (True, "{}_erofs_share_dup_blocks", "erofs_share_dup_blocks"),
+      (True, "{}_extfs_inode_count", "extfs_inode_count"),
+      (True, "{}_f2fs_compress", "f2fs_compress"),
+      (True, "{}_f2fs_sldc_flags", "f2fs_sldc_flags"),
+      (True, "{}_reserved_size", "partition_reserved_size"),
+      (True, "{}_squashfs_block_size", "squashfs_block_size"),
+      (True, "{}_squashfs_compressor", "squashfs_compressor"),
+      (True, "{}_squashfs_compressor_opt", "squashfs_compressor_opt"),
+      (True, "{}_squashfs_disable_4k_align", "squashfs_disable_4k_align"),
+      (True, "{}_verity_block_device", "verity_block_device"),
+  )
+
+  # Translate prefixed properties into generic ones.
+  if mount_point == "data":
+    prefix = "userdata"
+  else:
+    prefix = mount_point
+
+  for readonly, src_prop, dest_prop in fmt_props:
+    if readonly and mount_point not in ro_mount_points:
+      continue
+
+    if src_prop == "fs_type":
+      # This property is legacy and only used on a few partitions. b/202600377
+      allowed_partitions = set(["system", "system_other", "data", "oem"])
+      if mount_point not in allowed_partitions:
+          continue
+
+    if mount_point == "system_other":
+      # Propagate system properties to system_other. They'll get overridden
+      # after as needed.
+      copy_prop(src_prop.format("system"), dest_prop)
+
+    copy_prop(src_prop.format(prefix), dest_prop)
+
+  # Set prefixed properties that need a default value.
+  if mount_point in ro_mount_points:
+    prop = "{}_journal_size".format(prefix)
+    if not copy_prop(prop, "journal_size"):
+      d["journal_size"] = "0"
+
+    prop = "{}_extfs_rsv_pct".format(prefix)
+    if not copy_prop(prop, "extfs_rsv_pct"):
+      d["extfs_rsv_pct"] = "0"
+
+  # Copy partition-specific properties.
   d["mount_point"] = mount_point
   if mount_point == "system":
-    copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_system_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_system_key_path", "avb_key_path")
-    copy_prop("avb_system_algorithm", "avb_algorithm")
-    copy_prop("avb_system_salt", "avb_salt")
-    copy_prop("fs_type", "fs_type")
-    # Copy the generic system fs type first, override with specific one if
-    # available.
-    copy_prop("system_fs_type", "fs_type")
     copy_prop("system_headroom", "partition_headroom")
-    copy_prop("system_size", "partition_size")
-    if not copy_prop("system_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("system_verity_block_device", "verity_block_device")
     copy_prop("system_root_image", "system_root_image")
     copy_prop("root_dir", "root_dir")
     copy_prop("root_fs_config", "root_fs_config")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("system_f2fs_compress", "f2fs_compress")
-    copy_prop("system_f2fs_sldc_flags", "f2fs_sldc_flags")
-    copy_prop("system_squashfs_compressor", "squashfs_compressor")
-    copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("system_squashfs_block_size", "squashfs_block_size")
-    copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("system_base_fs_file", "base_fs_file")
-    copy_prop("system_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("system_reserved_size", "partition_reserved_size")
-    copy_prop("system_selinux_fc", "selinux_fc")
-  elif mount_point == "system_other":
-    # We inherit the selinux policies of /system since we contain some of its
-    # files.
-    copy_prop("avb_system_other_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_system_other_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_system_other_key_path", "avb_key_path")
-    copy_prop("avb_system_other_algorithm", "avb_algorithm")
-    copy_prop("avb_system_other_salt", "avb_salt")
-    copy_prop("fs_type", "fs_type")
-    copy_prop("system_fs_type", "fs_type")
-    copy_prop("system_other_size", "partition_size")
-    if not copy_prop("system_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("system_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("system_f2fs_compress", "f2fs_compress")
-    copy_prop("system_f2fs_sldc_flags", "f2fs_sldc_flags")
-    copy_prop("system_squashfs_compressor", "squashfs_compressor")
-    copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("system_squashfs_block_size", "squashfs_block_size")
-    copy_prop("system_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("system_reserved_size", "partition_reserved_size")
-    copy_prop("system_selinux_fc", "selinux_fc")
   elif mount_point == "data":
     # Copy the generic fs type first, override with specific one if available.
-    copy_prop("fs_type", "fs_type")
-    copy_prop("userdata_fs_type", "fs_type")
-    copy_prop("userdata_size", "partition_size")
     copy_prop("flash_logical_block_size", "flash_logical_block_size")
     copy_prop("flash_erase_block_size", "flash_erase_block_size")
-    copy_prop("userdata_selinux_fc", "selinux_fc")
     copy_prop("needs_casefold", "needs_casefold")
     copy_prop("needs_projid", "needs_projid")
     copy_prop("needs_compress", "needs_compress")
-  elif mount_point == "cache":
-    copy_prop("cache_fs_type", "fs_type")
-    copy_prop("cache_size", "partition_size")
-    copy_prop("cache_selinux_fc", "selinux_fc")
-  elif mount_point == "vendor":
-    copy_prop("avb_vendor_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_vendor_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_vendor_key_path", "avb_key_path")
-    copy_prop("avb_vendor_algorithm", "avb_algorithm")
-    copy_prop("avb_vendor_salt", "avb_salt")
-    copy_prop("vendor_fs_type", "fs_type")
-    copy_prop("vendor_size", "partition_size")
-    if not copy_prop("vendor_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("vendor_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("vendor_f2fs_compress", "f2fs_compress")
-    copy_prop("vendor_f2fs_sldc_flags", "f2fs_sldc_flags")
-    copy_prop("vendor_squashfs_compressor", "squashfs_compressor")
-    copy_prop("vendor_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("vendor_squashfs_block_size", "squashfs_block_size")
-    copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("vendor_base_fs_file", "base_fs_file")
-    copy_prop("vendor_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("vendor_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("vendor_reserved_size", "partition_reserved_size")
-    copy_prop("vendor_selinux_fc", "selinux_fc")
-  elif mount_point == "product":
-    copy_prop("avb_product_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_product_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_product_key_path", "avb_key_path")
-    copy_prop("avb_product_algorithm", "avb_algorithm")
-    copy_prop("avb_product_salt", "avb_salt")
-    copy_prop("product_fs_type", "fs_type")
-    copy_prop("product_size", "partition_size")
-    if not copy_prop("product_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("product_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("product_f2fs_compress", "f2fs_compress")
-    copy_prop("product_f2fs_sldc_flags", "f2fs_sldc_flags")
-    copy_prop("product_squashfs_compressor", "squashfs_compressor")
-    copy_prop("product_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("product_squashfs_block_size", "squashfs_block_size")
-    copy_prop("product_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("product_base_fs_file", "base_fs_file")
-    copy_prop("product_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("product_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("product_reserved_size", "partition_reserved_size")
-    copy_prop("product_selinux_fc", "selinux_fc")
-  elif mount_point == "system_ext":
-    copy_prop("avb_system_ext_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_system_ext_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_system_ext_key_path", "avb_key_path")
-    copy_prop("avb_system_ext_algorithm", "avb_algorithm")
-    copy_prop("avb_system_ext_salt", "avb_salt")
-    copy_prop("system_ext_fs_type", "fs_type")
-    copy_prop("system_ext_size", "partition_size")
-    if not copy_prop("system_ext_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("system_ext_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("system_ext_f2fs_compress", "f2fs_compress")
-    copy_prop("system_ext_f2fs_sldc_flags", "f2fs_sldc_flags")
-    copy_prop("system_ext_squashfs_compressor", "squashfs_compressor")
-    copy_prop("system_ext_squashfs_compressor_opt",
-              "squashfs_compressor_opt")
-    copy_prop("system_ext_squashfs_block_size", "squashfs_block_size")
-    copy_prop("system_ext_squashfs_disable_4k_align",
-              "squashfs_disable_4k_align")
-    copy_prop("system_ext_base_fs_file", "base_fs_file")
-    copy_prop("system_ext_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("system_ext_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("system_ext_reserved_size", "partition_reserved_size")
-    copy_prop("system_ext_selinux_fc", "selinux_fc")
-  elif mount_point == "odm":
-    copy_prop("avb_odm_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_odm_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_odm_key_path", "avb_key_path")
-    copy_prop("avb_odm_algorithm", "avb_algorithm")
-    copy_prop("avb_odm_salt", "avb_salt")
-    copy_prop("odm_fs_type", "fs_type")
-    copy_prop("odm_size", "partition_size")
-    if not copy_prop("odm_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("odm_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("odm_squashfs_compressor", "squashfs_compressor")
-    copy_prop("odm_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("odm_squashfs_block_size", "squashfs_block_size")
-    copy_prop("odm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("odm_base_fs_file", "base_fs_file")
-    copy_prop("odm_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("odm_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("odm_reserved_size", "partition_reserved_size")
-    copy_prop("odm_selinux_fc", "selinux_fc")
-  elif mount_point == "vendor_dlkm":
-    copy_prop("avb_vendor_dlkm_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_vendor_dlkm_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_vendor_dlkm_key_path", "avb_key_path")
-    copy_prop("avb_vendor_dlkm_algorithm", "avb_algorithm")
-    copy_prop("avb_vendor_dlkm_salt", "avb_salt")
-    copy_prop("vendor_dlkm_fs_type", "fs_type")
-    copy_prop("vendor_dlkm_size", "partition_size")
-    copy_prop("vendor_dlkm_f2fs_compress", "f2fs_compress")
-    copy_prop("vendor_dlkm_f2fs_sldc_flags", "f2fs_sldc_flags")
-    if not copy_prop("vendor_dlkm_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("vendor_dlkm_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("vendor_dlkm_squashfs_compressor", "squashfs_compressor")
-    copy_prop("vendor_dlkm_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("vendor_dlkm_squashfs_block_size", "squashfs_block_size")
-    copy_prop("vendor_dlkm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("vendor_dlkm_base_fs_file", "base_fs_file")
-    copy_prop("vendor_dlkm_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("vendor_dlkm_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("vendor_dlkm_reserved_size", "partition_reserved_size")
-    copy_prop("vendor_dlkm_selinux_fc", "selinux_fc")
-  elif mount_point == "odm_dlkm":
-    copy_prop("avb_odm_dlkm_hashtree_enable", "avb_hashtree_enable")
-    copy_prop("avb_odm_dlkm_add_hashtree_footer_args",
-              "avb_add_hashtree_footer_args")
-    copy_prop("avb_odm_dlkm_key_path", "avb_key_path")
-    copy_prop("avb_odm_dlkm_algorithm", "avb_algorithm")
-    copy_prop("avb_odm_dlkm_salt", "avb_salt")
-    copy_prop("odm_dlkm_fs_type", "fs_type")
-    copy_prop("odm_dlkm_size", "partition_size")
-    if not copy_prop("odm_dlkm_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("odm_dlkm_verity_block_device", "verity_block_device")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    copy_prop("odm_dlkm_squashfs_compressor", "squashfs_compressor")
-    copy_prop("odm_dlkm_squashfs_compressor_opt", "squashfs_compressor_opt")
-    copy_prop("odm_dlkm_squashfs_block_size", "squashfs_block_size")
-    copy_prop("odm_dlkm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
-    copy_prop("odm_dlkm_base_fs_file", "base_fs_file")
-    copy_prop("odm_dlkm_extfs_inode_count", "extfs_inode_count")
-    if not copy_prop("odm_dlkm_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("odm_dlkm_reserved_size", "partition_reserved_size")
-    copy_prop("odm_dlkm_selinux_fc", "selinux_fc")
-  elif mount_point == "oem":
-    copy_prop("fs_type", "fs_type")
-    copy_prop("oem_size", "partition_size")
-    if not copy_prop("oem_journal_size", "journal_size"):
-      d["journal_size"] = "0"
-    copy_prop("oem_extfs_inode_count", "extfs_inode_count")
-    copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
-    if not copy_prop("oem_extfs_rsv_pct", "extfs_rsv_pct"):
-      d["extfs_rsv_pct"] = "0"
-    copy_prop("oem_selinux_fc", "selinux_fc")
   d["partition_name"] = mount_point
   return d
 
diff --git a/tools/releasetools/care_map_pb2.py b/tools/releasetools/care_map_pb2.py
new file mode 100644
index 0000000..06aee25
--- /dev/null
+++ b/tools/releasetools/care_map_pb2.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: bootable/recovery/update_verifier/care_map.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='bootable/recovery/update_verifier/care_map.proto',
+  package='recovery_update_verifier',
+  syntax='proto3',
+  serialized_options=_b('H\003'),
+  serialized_pb=_b('\n0bootable/recovery/update_verifier/care_map.proto\x12\x18recovery_update_verifier\"\x9e\x01\n\x07\x43\x61reMap\x12\x43\n\npartitions\x18\x01 \x03(\x0b\x32/.recovery_update_verifier.CareMap.PartitionInfo\x1aN\n\rPartitionInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06ranges\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\t\x12\x13\n\x0b\x66ingerprint\x18\x04 \x01(\tB\x02H\x03\x62\x06proto3')
+)
+
+
+
+
+_CAREMAP_PARTITIONINFO = _descriptor.Descriptor(
+  name='PartitionInfo',
+  full_name='recovery_update_verifier.CareMap.PartitionInfo',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='name', full_name='recovery_update_verifier.CareMap.PartitionInfo.name', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='ranges', full_name='recovery_update_verifier.CareMap.PartitionInfo.ranges', index=1,
+      number=2, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='id', full_name='recovery_update_verifier.CareMap.PartitionInfo.id', index=2,
+      number=3, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='fingerprint', full_name='recovery_update_verifier.CareMap.PartitionInfo.fingerprint', index=3,
+      number=4, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=159,
+  serialized_end=237,
+)
+
+_CAREMAP = _descriptor.Descriptor(
+  name='CareMap',
+  full_name='recovery_update_verifier.CareMap',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='partitions', full_name='recovery_update_verifier.CareMap.partitions', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[_CAREMAP_PARTITIONINFO, ],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=79,
+  serialized_end=237,
+)
+
+_CAREMAP_PARTITIONINFO.containing_type = _CAREMAP
+_CAREMAP.fields_by_name['partitions'].message_type = _CAREMAP_PARTITIONINFO
+DESCRIPTOR.message_types_by_name['CareMap'] = _CAREMAP
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+CareMap = _reflection.GeneratedProtocolMessageType('CareMap', (_message.Message,), {
+
+  'PartitionInfo' : _reflection.GeneratedProtocolMessageType('PartitionInfo', (_message.Message,), {
+    'DESCRIPTOR' : _CAREMAP_PARTITIONINFO,
+    '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'
+    # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap.PartitionInfo)
+    })
+  ,
+  'DESCRIPTOR' : _CAREMAP,
+  '__module__' : 'bootable.recovery.update_verifier.care_map_pb2'
+  # @@protoc_insertion_point(class_scope:recovery_update_verifier.CareMap)
+  })
+_sym_db.RegisterMessage(CareMap)
+_sym_db.RegisterMessage(CareMap.PartitionInfo)
+
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index f678d08..2ee4b8e 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -80,11 +80,6 @@
     self.boot_signer_args = []
     self.verity_signer_path = None
     self.verity_signer_args = []
-    self.aftl_tool_path = None
-    self.aftl_server = None
-    self.aftl_key_path = None
-    self.aftl_manufacturer_key_path = None
-    self.aftl_signer_helper = None
     self.verbose = False
     self.tempfiles = []
     self.device_specific = None
@@ -276,6 +271,9 @@
     args = args[:]
     args[0] = FindHostToolPath(args[0])
 
+  if verbose is None:
+    verbose = OPTIONS.verbose
+
   # Don't log any if caller explicitly says so.
   if verbose:
     logger.info("  Running: \"%s\"", " ".join(args))
@@ -451,6 +449,13 @@
     return vabc_enabled
 
   @property
+  def is_vabc_xor(self):
+    vendor_prop = self.info_dict.get("vendor.build.prop")
+    vabc_xor_enabled = vendor_prop and \
+        vendor_prop.GetProp("ro.virtual_ab.compression.xor.enabled") == "true"
+    return vabc_xor_enabled
+
+  @property
   def vendor_suppressed_vabc(self):
     vendor_prop = self.info_dict.get("vendor.build.prop")
     vabc_suppressed = vendor_prop and \
@@ -461,6 +466,10 @@
   def oem_props(self):
     return self._oem_props
 
+  @property
+  def avb_enabled(self):
+    return self.get("avb_enable") == "true"
+
   def __getitem__(self, key):
     return self.info_dict[key]
 
@@ -1380,46 +1389,6 @@
   return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
 
 
-def ConstructAftlMakeImageCommands(output_image):
-  """Constructs the command to append the aftl image to vbmeta."""
-
-  # Ensure the other AFTL parameters are set as well.
-  assert OPTIONS.aftl_tool_path is not None, 'No aftl tool provided.'
-  assert OPTIONS.aftl_key_path is not None, 'No AFTL key provided.'
-  assert OPTIONS.aftl_manufacturer_key_path is not None, \
-      'No AFTL manufacturer key provided.'
-
-  vbmeta_image = MakeTempFile()
-  os.rename(output_image, vbmeta_image)
-  build_info = BuildInfo(OPTIONS.info_dict, use_legacy_id=True)
-  version_incremental = build_info.GetBuildProp("ro.build.version.incremental")
-  aftltool = OPTIONS.aftl_tool_path
-  server_argument_list = [OPTIONS.aftl_server, OPTIONS.aftl_key_path]
-  aftl_cmd = [aftltool, "make_icp_from_vbmeta",
-              "--vbmeta_image_path", vbmeta_image,
-              "--output", output_image,
-              "--version_incremental", version_incremental,
-              "--transparency_log_servers", ','.join(server_argument_list),
-              "--manufacturer_key", OPTIONS.aftl_manufacturer_key_path,
-              "--algorithm", "SHA256_RSA4096",
-              "--padding", "4096"]
-  if OPTIONS.aftl_signer_helper:
-    aftl_cmd.extend(shlex.split(OPTIONS.aftl_signer_helper))
-  return aftl_cmd
-
-
-def AddAftlInclusionProof(output_image):
-  """Appends the aftl inclusion proof to the vbmeta image."""
-
-  aftl_cmd = ConstructAftlMakeImageCommands(output_image)
-  RunAndCheckOutput(aftl_cmd)
-
-  verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
-                output_image, '--transparency_log_pub_keys',
-                OPTIONS.aftl_key_path]
-  RunAndCheckOutput(verify_cmd)
-
-
 def AppendGkiSigningArgs(cmd):
   """Append GKI signing arguments for mkbootimg."""
   # e.g., --gki_signing_key path/to/signing_key
@@ -1513,10 +1482,6 @@
 
   RunAndCheckOutput(cmd)
 
-  # Generate the AFTL inclusion proof.
-  if OPTIONS.aftl_server is not None:
-    AddAftlInclusionProof(image_path)
-
 
 def _MakeRamdisk(sourcedir, fs_config_file=None,
                  ramdisk_format=RamdiskFormat.GZ):
@@ -1960,14 +1925,14 @@
   RunAndCheckOutput(cmd)
 
 
-def UnzipTemp(filename, pattern=None):
+def UnzipTemp(filename, patterns=None):
   """Unzips the given archive into a temporary directory and returns the name.
 
   Args:
     filename: If filename is of the form "foo.zip+bar.zip", unzip foo.zip into
     a temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
 
-    pattern: Files to unzip from the archive. If omitted, will unzip the entire
+    patterns: Files to unzip from the archive. If omitted, will unzip the entire
     archvie.
 
   Returns:
@@ -1977,11 +1942,11 @@
   tmp = MakeTempDir(prefix="targetfiles-")
   m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
   if m:
-    UnzipToDir(m.group(1), tmp, pattern)
-    UnzipToDir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"), pattern)
+    UnzipToDir(m.group(1), tmp, patterns)
+    UnzipToDir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"), patterns)
     filename = m.group(1)
   else:
-    UnzipToDir(filename, tmp, pattern)
+    UnzipToDir(filename, tmp, patterns)
 
   return tmp
 
@@ -2018,6 +1983,8 @@
     info_dict = LoadInfoDict(input_zip)
 
   is_sparse = info_dict.get("extfs_sparse_flag")
+  if info_dict.get(which + "_disable_sparse"):
+    is_sparse = False
 
   # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
   # shared blocks (i.e. some blocks will show up in multiple files' block
@@ -2140,7 +2107,7 @@
   devnull = open("/dev/null", "w+b")
   for k in sorted(keylist):
     # We don't need a password for things that aren't really keys.
-    if k in SPECIAL_CERT_STRINGS:
+    if k in SPECIAL_CERT_STRINGS or k is None:
       no_passwords.append(k)
       continue
 
@@ -2474,9 +2441,7 @@
          "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=",
-         "extra=", "logfile=", "aftl_tool_path=", "aftl_server=",
-         "aftl_key_path=", "aftl_manufacturer_key_path=",
-         "aftl_signer_helper="] + list(extra_long_opts))
+         "extra=", "logfile="] + list(extra_long_opts))
   except getopt.GetoptError as err:
     Usage(docstring)
     print("**", str(err), "**")
@@ -2514,16 +2479,6 @@
       OPTIONS.verity_signer_path = a
     elif o in ("--verity_signer_args",):
       OPTIONS.verity_signer_args = shlex.split(a)
-    elif o in ("--aftl_tool_path",):
-      OPTIONS.aftl_tool_path = a
-    elif o in ("--aftl_server",):
-      OPTIONS.aftl_server = a
-    elif o in ("--aftl_key_path",):
-      OPTIONS.aftl_key_path = a
-    elif o in ("--aftl_manufacturer_key_path",):
-      OPTIONS.aftl_manufacturer_key_path = a
-    elif o in ("--aftl_signer_helper",):
-      OPTIONS.aftl_signer_helper = a
     elif o in ("-s", "--device_specific"):
       OPTIONS.device_specific = a
     elif o in ("-x", "--extra"):
@@ -2991,7 +2946,7 @@
           th.join()
 
       if p.returncode != 0:
-        logger.warning("Failure running %s:\n%s\n", diff_program, "".join(err))
+        logger.warning("Failure running %s:\n%s\n", cmd, "".join(err))
         self.patch = None
         return None, None, None
       diff = ptemp.read()
@@ -3898,12 +3853,14 @@
   if not image_size:
     return None
 
+  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)
 
   # For sparse images, we will only check the blocks that are listed in the care
   # map, i.e. the ones with meaningful data.
-  if "extfs_sparse_flag" in OPTIONS.info_dict:
+  if "extfs_sparse_flag" in OPTIONS.info_dict and not disable_sparse:
     simg = sparse_img.SparseImage(imgname)
     care_map_ranges = simg.care_map.intersect(
         rangelib.RangeSet("0-{}".format(image_blocks)))
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 5e6c42d..46ffdb7 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -78,13 +78,33 @@
       If provided, duplicate APK/APEX keys are ignored and the value from the
       framework is used.
 
+  --rebuild-sepolicy
+      If provided, rebuilds odm.img or vendor.img to include merged sepolicy
+      files. If odm is present then odm is preferred.
+
+  --vendor-otatools otatools.zip
+      If provided, use this otatools.zip when recompiling the odm or vendor
+      image to include sepolicy.
+
   --keep-tmp
       Keep tempoary files for debugging purposes.
+
+  The following only apply when using the VSDK to perform dexopt on vendor apps:
+
+  --framework-dexpreopt-config
+      If provided, the location of framwework's dexpreopt_config.zip.
+
+  --framework-dexpreopt-tools
+      if provided, the location of framework's dexpreopt_tools.zip.
+
+  --vendor-dexpreopt-config
+      If provided, the location of vendor's dexpreopt_config.zip.
 """
 
 from __future__ import print_function
 
 import fnmatch
+import glob
 import json
 import logging
 import os
@@ -129,7 +149,12 @@
 OPTIONS.rebuild_recovery = False
 # TODO(b/150582573): Remove this option.
 OPTIONS.allow_duplicate_apkapex_keys = False
+OPTIONS.vendor_otatools = None
+OPTIONS.rebuild_sepolicy = False
 OPTIONS.keep_tmp = False
+OPTIONS.framework_dexpreopt_config = None
+OPTIONS.framework_dexpreopt_tools = None
+OPTIONS.vendor_dexpreopt_config = None
 
 # In an item list (framework or vendor), we may see entries that select whole
 # partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the
@@ -666,7 +691,7 @@
       os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
 
 
-def compile_split_sepolicy(product_out, partition_map, output_policy):
+def compile_split_sepolicy(product_out, partition_map):
   """Uses secilc to compile a split sepolicy file.
 
   Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
@@ -674,7 +699,6 @@
   Args:
     product_out: PRODUCT_OUT directory, containing partition directories.
     partition_map: A map of partition name -> relative path within product_out.
-    output_policy: The name of the output policy created by secilc.
 
   Returns:
     A command list that can be executed to create the compiled sepolicy.
@@ -709,7 +733,7 @@
   # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
   cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
   cmd.extend(['-c', kernel_sepolicy_version])
-  cmd.extend(['-o', output_policy])
+  cmd.extend(['-o', os.path.join(product_out, 'META/combined_sepolicy')])
   cmd.extend(['-f', '/dev/null'])
 
   required_policy_files = (
@@ -747,7 +771,8 @@
   Depends on the <partition>/apex/* APEX files within partitions.
 
   Args:
-    output_target_files_dir: Output directory containing merged partition directories.
+    output_target_files_dir: Output directory containing merged partition
+      directories.
     partitions: A list of all the partitions in the output directory.
 
   Raises:
@@ -805,21 +830,23 @@
       PARTITIONS_WITH_CARE_MAP, partition_image_map)
 
 
-def process_special_cases(framework_target_files_temp_dir,
-                          vendor_target_files_temp_dir,
+def process_special_cases(temp_dir, framework_meta, vendor_meta,
                           output_target_files_temp_dir,
                           framework_misc_info_keys, framework_partition_set,
-                          vendor_partition_set):
+                          vendor_partition_set, framework_dexpreopt_tools,
+                          framework_dexpreopt_config, vendor_dexpreopt_config):
   """Performs special-case processing for certain target files items.
 
   Certain files in the output target files package require special-case
   processing. This function performs all that special-case processing.
 
   Args:
-    framework_target_files_temp_dir: The name of a directory containing the
-      special items extracted from the framework target files package.
-    vendor_target_files_temp_dir: The name of a directory containing the special
-      items extracted from the vendor target files package.
+    temp_dir: Location containing an 'output' directory where target files have
+      been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES, etc.
+    framework_meta: The name of a directory containing the special items
+      extracted from the framework target files package.
+    vendor_meta: The name of a directory containing the special items
+      extracted from the vendor target files package.
     output_target_files_temp_dir: The name of a directory that will be used to
       create the output target files package after all the special cases are
       processed.
@@ -830,50 +857,361 @@
       partitions. Used to filter apexkeys.txt and apkcerts.txt.
     vendor_partition_set: Partitions that are considered vendor partitions. Used
       to filter apexkeys.txt and apkcerts.txt.
+
+    The following are only used if dexpreopt is applied:
+
+    framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+    framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+    vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
   """
 
   if 'ab_update' in framework_misc_info_keys:
     process_ab_partitions_txt(
-        framework_target_files_temp_dir=framework_target_files_temp_dir,
-        vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+        framework_target_files_temp_dir=framework_meta,
+        vendor_target_files_temp_dir=vendor_meta,
         output_target_files_temp_dir=output_target_files_temp_dir)
 
   copy_file_contexts(
-      framework_target_files_dir=framework_target_files_temp_dir,
-      vendor_target_files_dir=vendor_target_files_temp_dir,
+      framework_target_files_dir=framework_meta,
+      vendor_target_files_dir=vendor_meta,
       output_target_files_dir=output_target_files_temp_dir)
 
   process_misc_info_txt(
-      framework_target_files_temp_dir=framework_target_files_temp_dir,
-      vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+      framework_target_files_temp_dir=framework_meta,
+      vendor_target_files_temp_dir=vendor_meta,
       output_target_files_temp_dir=output_target_files_temp_dir,
       framework_misc_info_keys=framework_misc_info_keys)
 
   process_dynamic_partitions_info_txt(
-      framework_target_files_dir=framework_target_files_temp_dir,
-      vendor_target_files_dir=vendor_target_files_temp_dir,
+      framework_target_files_dir=framework_meta,
+      vendor_target_files_dir=vendor_meta,
       output_target_files_dir=output_target_files_temp_dir)
 
   process_apex_keys_apk_certs_common(
-      framework_target_files_dir=framework_target_files_temp_dir,
-      vendor_target_files_dir=vendor_target_files_temp_dir,
+      framework_target_files_dir=framework_meta,
+      vendor_target_files_dir=vendor_meta,
       output_target_files_dir=output_target_files_temp_dir,
       framework_partition_set=framework_partition_set,
       vendor_partition_set=vendor_partition_set,
       file_name='apkcerts.txt')
 
   process_apex_keys_apk_certs_common(
-      framework_target_files_dir=framework_target_files_temp_dir,
-      vendor_target_files_dir=vendor_target_files_temp_dir,
+      framework_target_files_dir=framework_meta,
+      vendor_target_files_dir=vendor_meta,
       output_target_files_dir=output_target_files_temp_dir,
       framework_partition_set=framework_partition_set,
       vendor_partition_set=vendor_partition_set,
       file_name='apexkeys.txt')
 
+  process_dexopt(
+      temp_dir=temp_dir,
+      framework_meta=framework_meta,
+      vendor_meta=vendor_meta,
+      output_target_files_temp_dir=output_target_files_temp_dir,
+      framework_dexpreopt_tools=framework_dexpreopt_tools,
+      framework_dexpreopt_config=framework_dexpreopt_config,
+      vendor_dexpreopt_config=vendor_dexpreopt_config)
+
+
+def process_dexopt(temp_dir, framework_meta, vendor_meta,
+                   output_target_files_temp_dir,
+                   framework_dexpreopt_tools, framework_dexpreopt_config,
+                   vendor_dexpreopt_config):
+  """If needed, generates dexopt files for vendor apps.
+
+  Args:
+    temp_dir: Location containing an 'output' directory where target files have
+      been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES, etc.
+    framework_meta: The name of a directory containing the special items
+      extracted from the framework target files package.
+    vendor_meta: The name of a directory containing the special items extracted
+      from the vendor target files package.
+    output_target_files_temp_dir: The name of a directory that will be used to
+      create the output target files package after all the special cases are
+      processed.
+    framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+    framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+    vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
+  """
+  # Load vendor and framework META/misc_info.txt.
+  misc_info_path = ['META', 'misc_info.txt']
+  vendor_misc_info_dict = common.LoadDictionaryFromFile(
+      os.path.join(vendor_meta, *misc_info_path))
+
+  if (vendor_misc_info_dict.get('building_with_vsdk') != 'true' or
+      framework_dexpreopt_tools is None or
+      framework_dexpreopt_config is None or
+      vendor_dexpreopt_config is None):
+    return
+
+  logger.info('applying dexpreopt')
+
+  # The directory structure to apply dexpreopt is:
+  #
+  # <temp_dir>/
+  #     framework_meta/
+  #         META/
+  #     vendor_meta/
+  #         META/
+  #     output/
+  #         SYSTEM/
+  #         VENDOR/
+  #         IMAGES/
+  #         <other items extracted from system and vendor target files>
+  #     tools/
+  #         <contents of dexpreopt_tools.zip>
+  #     system_config/
+  #         <contents of system dexpreopt_config.zip>
+  #     vendor_config/
+  #         <contents of vendor dexpreopt_config.zip>
+  #     system -> output/SYSTEM
+  #     vendor -> output/VENDOR
+  #     apex -> output/SYSTEM/apex (only for flattened APEX builds)
+  #     apex/ (extracted updatable APEX)
+  #         <apex 1>/
+  #             ...
+  #         <apex 2>/
+  #             ...
+  #         ...
+  #     out/dex2oat_result/vendor/
+  #         <app>
+  #             oat/arm64/
+  #                 package.vdex
+  #                 package.odex
+  #         <priv-app>
+  #             oat/arm64/
+  #                 package.vdex
+  #                 package.odex
+  dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')
+  dexpreopt_framework_config_files_temp_dir = os.path.join(temp_dir, 'system_config')
+  dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir, 'vendor_config')
+
+  extract_items(
+      target_files=OPTIONS.framework_dexpreopt_tools,
+      target_files_temp_dir=dexpreopt_tools_files_temp_dir,
+      extract_item_list=('*',))
+  extract_items(
+      target_files=OPTIONS.framework_dexpreopt_config,
+      target_files_temp_dir=dexpreopt_framework_config_files_temp_dir,
+      extract_item_list=('*',))
+  extract_items(
+      target_files=OPTIONS.vendor_dexpreopt_config,
+      target_files_temp_dir=dexpreopt_vendor_config_files_temp_dir,
+      extract_item_list=('*',))
+
+  os.symlink(os.path.join(output_target_files_temp_dir, "SYSTEM"),
+             os.path.join(temp_dir, "system"))
+  os.symlink(os.path.join(output_target_files_temp_dir, "VENDOR"),
+             os.path.join(temp_dir, "vendor"))
+
+  # The directory structure for flatteded APEXes is:
+  #
+  # SYSTEM
+  #     apex
+  #         <APEX name, e.g., com.android.wifi>
+  #             apex_manifest.pb
+  #             apex_pubkey
+  #             etc/
+  #             javalib/
+  #             lib/
+  #             lib64/
+  #             priv-app/
+  #
+  # The directory structure for updatable APEXes is:
+  #
+  # SYSTEM
+  #     apex
+  #         com.android.adbd.apex
+  #         com.android.appsearch.apex
+  #         com.android.art.apex
+  #         ...
+  apex_root = os.path.join(output_target_files_temp_dir, "SYSTEM", "apex")
+  framework_misc_info_dict = common.LoadDictionaryFromFile(
+      os.path.join(framework_meta, *misc_info_path))
+
+  # Check for flattended versus updatable APEX.
+  if framework_misc_info_dict.get('target_flatten_apex') == 'false':
+    # Extract APEX.
+    logging.info('extracting APEX')
+
+    apex_extract_root_dir = os.path.join(temp_dir, 'apex')
+    os.makedirs(apex_extract_root_dir)
+
+    for apex in (glob.glob(os.path.join(apex_root, '*.apex')) +
+                 glob.glob(os.path.join(apex_root, '*.capex'))):
+      logging.info('  apex: %s', apex)
+      # deapexer is in the same directory as the merge_target_files binary extracted
+      # from otatools.zip.
+      apex_json_info = subprocess.check_output(['deapexer', 'info', apex])
+      logging.info('    info: %s', apex_json_info)
+      apex_info = json.loads(apex_json_info)
+      apex_name = apex_info['name']
+      logging.info('    name: %s', apex_name)
+
+      apex_extract_dir = os.path.join(apex_extract_root_dir, apex_name)
+      os.makedirs(apex_extract_dir)
+
+      # deapexer uses debugfs_static, which is part of otatools.zip.
+      command = [
+          'deapexer',
+          '--debugfs_path',
+          'debugfs_static',
+          'extract',
+          apex,
+          apex_extract_dir,
+      ]
+      logging.info('    running %s', command)
+      subprocess.check_call(command)
+  else:
+    # Flattened APEXes don't need to be extracted since they have the necessary
+    # directory structure.
+    os.symlink(os.path.join(apex_root), os.path.join(temp_dir, 'apex'))
+
+  # Modify system config to point to the tools that have been extracted.
+  # Absolute or .. paths are not allowed  by the dexpreopt_gen tool in
+  # dexpreopt_soong.config.
+  dexpreopt_framework_soon_config = os.path.join(
+      dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')
+  with open(dexpreopt_framework_soon_config, 'w') as f:
+    dexpreopt_soong_config = {
+        'Profman': 'tools/profman',
+        'Dex2oat': 'tools/dex2oatd',
+        'Aapt': 'tools/aapt2',
+        'SoongZip': 'tools/soong_zip',
+        'Zip2zip': 'tools/zip2zip',
+        'ManifestCheck': 'tools/manifest_check',
+        'ConstructContext': 'tools/construct_context',
+    }
+    json.dump(dexpreopt_soong_config, f)
+
+  # TODO(b/188179859): Make *dex location configurable to vendor or system_other.
+  use_system_other_odex = False
+
+  if use_system_other_odex:
+    dex_img = 'SYSTEM_OTHER'
+  else:
+    dex_img = 'VENDOR'
+    # Open vendor_filesystem_config to append the items generated by dexopt.
+    vendor_file_system_config = open(
+        os.path.join(temp_dir, 'output', 'META', 'vendor_filesystem_config.txt'),
+        'a')
+
+  # Dexpreopt vendor apps.
+  dexpreopt_config_suffix = '_dexpreopt.config'
+  for config in glob.glob(os.path.join(
+      dexpreopt_vendor_config_files_temp_dir, '*' + dexpreopt_config_suffix)):
+    app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]
+    logging.info('dexpreopt config: %s %s', config, app)
+
+    apk_dir = 'app'
+    apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
+    if not os.path.exists(apk_path):
+      apk_dir = 'priv-app'
+      apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
+      if not os.path.exists(apk_path):
+        logging.warning('skipping dexpreopt for %s, no apk found in vendor/app '
+                        'or vendor/priv-app', app)
+        continue
+
+    # Generate dexpreopting script. Note 'out_dir' is not the output directory
+    # where the script is generated, but the OUT_DIR at build time referenced
+    # in the dexpreot config files, e.g., "out/.../core-oj.jar", so the tool knows
+    # how to adjust the path.
+    command = [
+        os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),
+        '-global',
+        os.path.join(dexpreopt_framework_config_files_temp_dir, 'dexpreopt.config'),
+        '-global_soong',
+        os.path.join(
+            dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config'),
+        '-module',
+        config,
+        '-dexpreopt_script',
+        'dexpreopt_app.sh',
+        '-out_dir',
+        'out',
+        '-base_path',
+        '.',
+        '--uses_target_files',
+    ]
+
+    # Run the command from temp_dir so all tool paths are its descendants.
+    logging.info("running %s", command)
+    subprocess.check_call(command, cwd = temp_dir)
+
+    # Call the generated script.
+    command = ['sh', 'dexpreopt_app.sh', apk_path]
+    logging.info("running %s", command)
+    subprocess.check_call(command, cwd = temp_dir)
+
+    # Output files are in:
+    #
+    # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex
+    # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex
+    # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex
+    # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex
+    #
+    # Copy the files to their destination. The structure of system_other is:
+    #
+    # system_other/
+    #     system-other-odex-marker
+    #     system/
+    #         app/
+    #             <app>/oat/arm64/
+    #                 <app>.odex
+    #                 <app>.vdex
+    #             ...
+    #         priv-app/
+    #             <app>/oat/arm64/
+    #                 <app>.odex
+    #                 <app>.vdex
+    #             ...
+
+    # TODO(b/188179859): Support for other architectures.
+    arch = 'arm64'
+
+    dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app, 'oat', arch)
+    os.makedirs(dex_destination)
+    dex2oat_path = os.path.join(
+        temp_dir, 'out', 'dex2oat_result', 'vendor', apk_dir, app, 'oat', arch)
+    shutil.copy(os.path.join(dex2oat_path, 'package.vdex'),
+                os.path.join(dex_destination, app + '.vdex'))
+    shutil.copy(os.path.join(dex2oat_path, 'package.odex'),
+                os.path.join(dex_destination, app + '.odex'))
+
+    # Append entries to vendor_file_system_config.txt, such as:
+    #
+    # vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+    # vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+    # vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+    # vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
+    if not use_system_other_odex:
+      vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'
+      selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'
+      vendor_file_system_config.writelines([
+          vendor_app_prefix + ' 0 2000 755 ' + selabel + '\n',
+          vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\n',
+          vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' + selabel + '\n',
+          vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' + selabel + '\n',
+      ])
+
+  if not use_system_other_odex:
+    vendor_file_system_config.close()
+    # Delete vendor.img so that it will be regenerated.
+    # TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)
+    #                    and S(vendor) may require logic similar to that in
+    #                    rebuild_image_with_sepolicy.
+    vendor_img = os.path.join(output_target_files_temp_dir, 'IMAGES', 'vendor.img')
+    if os.path.exists(vendor_img):
+      logging.info('Deleting %s', vendor_img)
+      os.remove(vendor_img)
+
 
 def create_merged_package(temp_dir, framework_target_files, framework_item_list,
                           vendor_target_files, vendor_item_list,
-                          framework_misc_info_keys, rebuild_recovery):
+                          framework_misc_info_keys, rebuild_recovery,
+                          framework_dexpreopt_tools, framework_dexpreopt_config,
+                          vendor_dexpreopt_config):
   """Merges two target files packages into one target files structure.
 
   Args:
@@ -898,6 +1236,12 @@
     rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
       devices and write it to the system image.
 
+    The following are only used if dexpreopt is applied:
+
+    framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+    framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+    vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
+
   Returns:
     Path to merged package under temp directory.
   """
@@ -918,23 +1262,27 @@
   # Perform special case processing on META/* items.
   # After this function completes successfully, all the files we need to create
   # the output target files package are in place.
-  framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
-  vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
+  framework_meta = os.path.join(temp_dir, 'framework_meta')
+  vendor_meta = os.path.join(temp_dir, 'vendor_meta')
   extract_items(
       target_files=framework_target_files,
-      target_files_temp_dir=framework_target_files_temp_dir,
+      target_files_temp_dir=framework_meta,
       extract_item_list=('META/*',))
   extract_items(
       target_files=vendor_target_files,
-      target_files_temp_dir=vendor_target_files_temp_dir,
+      target_files_temp_dir=vendor_meta,
       extract_item_list=('META/*',))
   process_special_cases(
-      framework_target_files_temp_dir=framework_target_files_temp_dir,
-      vendor_target_files_temp_dir=vendor_target_files_temp_dir,
+      temp_dir=temp_dir,
+      framework_meta=framework_meta,
+      vendor_meta=vendor_meta,
       output_target_files_temp_dir=output_target_files_temp_dir,
       framework_misc_info_keys=framework_misc_info_keys,
       framework_partition_set=item_list_to_partition_set(framework_item_list),
-      vendor_partition_set=item_list_to_partition_set(vendor_item_list))
+      vendor_partition_set=item_list_to_partition_set(vendor_item_list),
+      framework_dexpreopt_tools=framework_dexpreopt_tools,
+      framework_dexpreopt_config=framework_dexpreopt_config,
+      vendor_dexpreopt_config=vendor_dexpreopt_config)
 
   return output_target_files_temp_dir
 
@@ -965,6 +1313,102 @@
   add_img_to_target_files.main(add_img_args)
 
 
+def rebuild_image_with_sepolicy(target_files_dir,
+                                vendor_otatools=None,
+                                vendor_target_files=None):
+  """Rebuilds odm.img or vendor.img to include merged sepolicy files.
+
+  If odm is present then odm is preferred -- otherwise vendor is used.
+
+  Args:
+    target_files_dir: Path to the extracted merged target-files package.
+    vendor_otatools: If not None, path to an otatools.zip from the vendor build
+      that is used when recompiling the image.
+    vendor_target_files: Expected if vendor_otatools is not None. Path to the
+      vendor target-files zip.
+  """
+  partition = 'vendor'
+  if os.path.exists(os.path.join(target_files_dir, 'ODM')) or os.path.exists(
+      os.path.join(target_files_dir, 'IMAGES/odm.img')):
+    partition = 'odm'
+  partition_img = '{}.img'.format(partition)
+
+  logger.info('Recompiling %s using the merged sepolicy files.', partition_img)
+
+  # Copy the combined SEPolicy file and framework hashes to the image that is
+  # being rebuilt.
+  def copy_selinux_file(input_path, output_filename):
+    input_filename = os.path.join(target_files_dir, input_path)
+    if not os.path.exists(input_filename):
+      input_filename = input_filename.replace('SYSTEM_EXT/', 'SYSTEM/system_ext/') \
+          .replace('PRODUCT/', 'SYSTEM/product/')
+      if not os.path.exists(input_filename):
+        logger.info('Skipping copy_selinux_file for %s', input_filename)
+        return
+    shutil.copy(
+        input_filename,
+        os.path.join(target_files_dir, partition.upper(), 'etc/selinux',
+                     output_filename))
+
+  copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')
+  copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',
+                    'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')
+  copy_selinux_file(
+      'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',
+      'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')
+  copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',
+                    'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')
+
+  if not vendor_otatools:
+    # Remove the partition from the merged target-files archive. It will be
+    # rebuilt later automatically by generate_images().
+    os.remove(os.path.join(target_files_dir, 'IMAGES', partition_img))
+  else:
+    # TODO(b/192253131): Remove the need for vendor_otatools by fixing
+    # backwards-compatibility issues when compiling images on R from S+.
+    if not vendor_target_files:
+      raise ValueError(
+          'Expected vendor_target_files if vendor_otatools is not None.')
+    logger.info(
+        '%s recompilation will be performed using the vendor otatools.zip',
+        partition_img)
+
+    # Unzip the vendor build's otatools.zip and target-files archive.
+    vendor_otatools_dir = common.MakeTempDir(
+        prefix='merge_target_files_vendor_otatools_')
+    vendor_target_files_dir = common.MakeTempDir(
+        prefix='merge_target_files_vendor_target_files_')
+    common.UnzipToDir(vendor_otatools, vendor_otatools_dir)
+    common.UnzipToDir(vendor_target_files, vendor_target_files_dir)
+
+    # Copy the partition contents from the merged target-files archive to the
+    # vendor target-files archive.
+    shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))
+    shutil.copytree(
+        os.path.join(target_files_dir, partition.upper()),
+        os.path.join(vendor_target_files_dir, partition.upper()),
+        symlinks=True)
+
+    # Delete then rebuild the partition.
+    os.remove(os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
+    rebuild_partition_command = [
+        os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
+        '--verbose',
+        '--add_missing',
+        vendor_target_files_dir,
+    ]
+    logger.info('Recompiling %s: %s', partition_img,
+                ' '.join(rebuild_partition_command))
+    common.RunAndCheckOutput(rebuild_partition_command, verbose=True)
+
+    # Move the newly-created image to the merged target files dir.
+    if not os.path.exists(os.path.join(target_files_dir, 'IMAGES')):
+      os.makedirs(os.path.join(target_files_dir, 'IMAGES'))
+    shutil.move(
+        os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),
+        os.path.join(target_files_dir, 'IMAGES', partition_img))
+
+
 def generate_super_empty_image(target_dir, output_super_empty):
   """Generates super_empty image from target package.
 
@@ -1049,7 +1493,9 @@
                        framework_misc_info_keys, vendor_target_files,
                        vendor_item_list, output_target_files, output_dir,
                        output_item_list, output_ota, output_img,
-                       output_super_empty, rebuild_recovery):
+                       output_super_empty, rebuild_recovery, vendor_otatools,
+                       rebuild_sepolicy, framework_dexpreopt_tools,
+                       framework_dexpreopt_config, vendor_dexpreopt_config):
   """Merges two target files packages together.
 
   This function takes framework and vendor target files packages as input,
@@ -1085,6 +1531,15 @@
       merged target files package and saves it at this path.
     rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
       devices and write it to the system image.
+    vendor_otatools: Path to an otatools zip used for recompiling vendor images.
+    rebuild_sepolicy: If true, rebuild odm.img (if target uses ODM) or
+      vendor.img using a merged precompiled_sepolicy file.
+
+    The following are only used if dexpreopt is applied:
+
+    framework_dexpreopt_tools: Location of dexpreopt_tools.zip.
+    framework_dexpreopt_config: Location of framework's dexpreopt_config.zip.
+    vendor_dexpreopt_config: Location of vendor's dexpreopt_config.zip.
   """
 
   logger.info('starting: merge framework %s and vendor %s into output %s',
@@ -1093,7 +1548,8 @@
   output_target_files_temp_dir = create_merged_package(
       temp_dir, framework_target_files, framework_item_list,
       vendor_target_files, vendor_item_list, framework_misc_info_keys,
-      rebuild_recovery)
+      rebuild_recovery, framework_dexpreopt_tools, framework_dexpreopt_config,
+      vendor_dexpreopt_config)
 
   if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
     raise RuntimeError('Incompatible VINTF metadata')
@@ -1137,14 +1593,14 @@
       partition_map=filtered_partitions)
 
   # Check that the split sepolicy from the multiple builds can compile.
-  split_sepolicy_cmd = compile_split_sepolicy(
-      product_out=output_target_files_temp_dir,
-      partition_map=filtered_partitions,
-      output_policy=os.path.join(output_target_files_temp_dir,
-                                 'META/combined.policy'))
+  split_sepolicy_cmd = compile_split_sepolicy(output_target_files_temp_dir,
+                                              filtered_partitions)
   logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd))
   common.RunAndCheckOutput(split_sepolicy_cmd)
-  # TODO(b/178864050): Run tests on the combined.policy file.
+  # Include the compiled policy in an image if requested.
+  if rebuild_sepolicy:
+    rebuild_image_with_sepolicy(output_target_files_temp_dir, vendor_otatools,
+                                vendor_target_files)
 
   # Run validation checks on the pre-installed APEX files.
   validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
@@ -1261,8 +1717,18 @@
       OPTIONS.rebuild_recovery = True
     elif o == '--allow-duplicate-apkapex-keys':
       OPTIONS.allow_duplicate_apkapex_keys = True
+    elif o == '--vendor-otatools':
+      OPTIONS.vendor_otatools = a
+    elif o == '--rebuild-sepolicy':
+      OPTIONS.rebuild_sepolicy = True
     elif o == '--keep-tmp':
       OPTIONS.keep_tmp = True
+    elif o == '--framework-dexpreopt-config':
+      OPTIONS.framework_dexpreopt_config = a
+    elif o == '--framework-dexpreopt-tools':
+      OPTIONS.framework_dexpreopt_tools = a
+    elif o == '--vendor-dexpreopt-config':
+      OPTIONS.vendor_dexpreopt_config = a
     else:
       return False
     return True
@@ -1287,8 +1753,13 @@
           'output-ota=',
           'output-img=',
           'output-super-empty=',
+          'framework-dexpreopt-config=',
+          'framework-dexpreopt-tools=',
+          'vendor-dexpreopt-config=',
           'rebuild_recovery',
           'allow-duplicate-apkapex-keys',
+          'vendor-otatools=',
+          'rebuild-sepolicy',
           'keep-tmp',
       ],
       extra_option_handler=option_handler)
@@ -1342,7 +1813,12 @@
           output_ota=OPTIONS.output_ota,
           output_img=OPTIONS.output_img,
           output_super_empty=OPTIONS.output_super_empty,
-          rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
+          rebuild_recovery=OPTIONS.rebuild_recovery,
+          vendor_otatools=OPTIONS.vendor_otatools,
+          rebuild_sepolicy=OPTIONS.rebuild_sepolicy,
+          framework_dexpreopt_tools=OPTIONS.framework_dexpreopt_tools,
+          framework_dexpreopt_config=OPTIONS.framework_dexpreopt_config,
+          vendor_dexpreopt_config=OPTIONS.vendor_dexpreopt_config), OPTIONS.keep_tmp)
 
 
 if __name__ == '__main__':
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 42d1211..17f373e 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -221,6 +221,12 @@
       For VABC downgrades, we must finish merging before doing data wipe, and
       since data wipe is required for downgrading OTA, this might cause long
       wait time in recovery.
+
+  --enable_vabc_xor
+      Enable the VABC xor feature. Will reduce space requirements for OTA
+
+  --force_minor_version
+      Override the update_engine minor version for delta generation.
 """
 
 from __future__ import print_function
@@ -237,10 +243,11 @@
 import sys
 import zipfile
 
+import care_map_pb2
 import common
 import ota_utils
 from ota_utils import (UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata,
-                       PropertyFiles, SECURITY_PATCH_LEVEL_PROP_NAME)
+                       PropertyFiles, SECURITY_PATCH_LEVEL_PROP_NAME, GetZipEntryOffset)
 import target_files_diff
 from check_target_files_vintf import CheckVintfIfTrebleEnabled
 from non_ab_ota import GenerateNonAbOtaPackage
@@ -285,6 +292,8 @@
 OPTIONS.disable_vabc = False
 OPTIONS.spl_downgrade = False
 OPTIONS.vabc_downgrade = False
+OPTIONS.enable_vabc_xor = True
+OPTIONS.force_minor_version = None
 
 POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
 DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
@@ -529,6 +538,8 @@
         'payload_properties.txt',
     )
     self.optional = (
+        # apex_info.pb isn't directly used in the update flow
+        'apex_info.pb',
         # care_map is available only if dm-verity is enabled.
         'care_map.pb',
         'care_map.txt',
@@ -599,20 +610,20 @@
     payload, till the end of 'medatada_signature_message'.
     """
     payload_info = input_zip.getinfo('payload.bin')
-    payload_offset = payload_info.header_offset
-    payload_offset += zipfile.sizeFileHeader
-    payload_offset += len(payload_info.extra) + len(payload_info.filename)
-    payload_size = payload_info.file_size
+    (payload_offset, payload_size) = GetZipEntryOffset(input_zip, payload_info)
 
-    with input_zip.open('payload.bin') as payload_fp:
-      header_bin = payload_fp.read(24)
+    # Read the underlying raw zipfile at specified offset
+    payload_fp = input_zip.fp
+    payload_fp.seek(payload_offset)
+    header_bin = payload_fp.read(24)
 
     # network byte order (big-endian)
     header = struct.unpack("!IQQL", header_bin)
 
     # 'CrAU'
     magic = header[0]
-    assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
+    assert magic == 0x43724155, "Invalid magic: {:x}, computed offset {}" \
+        .format(magic, payload_offset)
 
     manifest_size = header[2]
     metadata_signature_size = header[3]
@@ -832,6 +843,17 @@
   with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
     common.ZipWriteStr(partial_target_zip, 'META/ab_partitions.txt',
                        '\n'.join(ab_partitions))
+    CARE_MAP_ENTRY = "META/care_map.pb"
+    if CARE_MAP_ENTRY in input_zip.namelist():
+      caremap = care_map_pb2.CareMap()
+      caremap.ParseFromString(input_zip.read(CARE_MAP_ENTRY))
+      filtered = [
+          part for part in caremap.partitions if part.name in ab_partitions]
+      del caremap.partitions[:]
+      caremap.partitions.extend(filtered)
+      common.ZipWriteStr(partial_target_zip, CARE_MAP_ENTRY,
+                         caremap.SerializeToString())
+
     for info_file in ['META/misc_info.txt', DYNAMIC_PARTITION_INFO]:
       if info_file not in input_zip.namelist():
         logger.warning('Cannot find %s in input zipfile', info_file)
@@ -841,7 +863,8 @@
           content, lambda p: p in ab_partitions)
       common.ZipWriteStr(partial_target_zip, info_file, modified_info)
 
-    # TODO(xunchang) handle 'META/care_map.pb', 'META/postinstall_config.txt'
+    # TODO(xunchang) handle META/postinstall_config.txt'
+
   common.ZipClose(partial_target_zip)
 
   return partial_target_file
@@ -1075,6 +1098,9 @@
   if target_info.vendor_suppressed_vabc:
     logger.info("Vendor suppressed VABC. Disabling")
     OPTIONS.disable_vabc = True
+  if not target_info.is_vabc_xor or OPTIONS.disable_vabc:
+    logger.info("VABC XOR Not supported, disabling")
+    OPTIONS.enable_vabc_xor = False
   additional_args = []
 
   # Prepare custom images.
@@ -1097,6 +1123,8 @@
     target_info.info_dict['ab_partitions'] = zfp.read(
         AB_PARTITIONS).decode().strip().split("\n")
 
+  CheckVintfIfTrebleEnabled(target_file, target_info)
+
   # Metadata to comply with Android OTA package format.
   metadata = GetPackageMetadata(target_info, source_info)
   # Generate payload.
@@ -1117,6 +1145,10 @@
 
   if OPTIONS.disable_vabc:
     additional_args += ["--disable_vabc", "true"]
+  if OPTIONS.enable_vabc_xor:
+    additional_args += ["--enable_vabc_xor", "true"]
+  if OPTIONS.force_minor_version:
+    additional_args += ["--force_minor_version", OPTIONS.force_minor_version]
   additional_args += ["--max_timestamp", max_timestamp]
 
   if SupportsMainlineGkiUpdates(source_file):
@@ -1170,19 +1202,15 @@
     else:
       logger.warning("Cannot find care map file in target_file package")
 
-  # Copy apex_info.pb over to generated OTA package.
-  try:
-    apex_info_entry = target_zip.getinfo("META/apex_info.pb")
-    with target_zip.open(apex_info_entry, "r") as zfp:
-      common.ZipWriteStr(output_zip, "apex_info.pb", zfp.read(),
-                         compress_type=zipfile.ZIP_STORED)
-  except KeyError:
-    logger.warning("target_file doesn't contain apex_info.pb %s", target_file)
+  # Add the source apex version for incremental ota updates, and write the
+  # result apex info to the ota package.
+  ota_apex_info = ota_utils.ConstructOtaApexInfo(target_zip, source_file)
+  if ota_apex_info is not None:
+    common.ZipWriteStr(output_zip, "apex_info.pb", ota_apex_info,
+                       compress_type=zipfile.ZIP_STORED)
 
   common.ZipClose(target_zip)
 
-  CheckVintfIfTrebleEnabled(target_file, target_info)
-
   # We haven't written the metadata entry yet, which will be handled in
   # FinalizeMetadata().
   common.ZipClose(output_zip)
@@ -1292,6 +1320,10 @@
       OPTIONS.wipe_user_data = True
     elif o == "--vabc_downgrade":
       OPTIONS.vabc_downgrade = True
+    elif o == "--enable_vabc_xor":
+      OPTIONS.enable_vabc_xor = a.lower() != "false"
+    elif o == "--force_minor_version":
+      OPTIONS.force_minor_version = a
     else:
       return False
     return True
@@ -1336,6 +1368,8 @@
                                  "disable_vabc",
                                  "spl_downgrade",
                                  "vabc_downgrade",
+                                 "enable_vabc_xor=",
+                                 "force_minor_version=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
@@ -1367,8 +1401,8 @@
     # We should only allow downgrading incrementals (as opposed to full).
     # Otherwise the device may go back from arbitrary build with this full
     # OTA package.
-    if OPTIONS.incremental_source is None:
-      raise ValueError("Cannot generate downgradable full OTAs")
+  if OPTIONS.incremental_source is None and OPTIONS.downgrade:
+    raise ValueError("Cannot generate downgradable full OTAs")
 
   # TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the
   # target-file and reload the info_dict. So the info will be consistent with
diff --git a/tools/releasetools/ota_metadata.proto b/tools/releasetools/ota_metadata.proto
index ed9d0c3..689ce80 100644
--- a/tools/releasetools/ota_metadata.proto
+++ b/tools/releasetools/ota_metadata.proto
@@ -72,6 +72,8 @@
   int64 version = 2;
   bool is_compressed = 3;
   int64 decompressed_size = 4;
+  // Used in OTA
+  int64 source_version = 5;
 }
 
 // Just a container to hold repeated apex_info, so that we can easily serialize
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 104f02f..5737009 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -16,6 +16,7 @@
 import itertools
 import logging
 import os
+import struct
 import zipfile
 
 import ota_metadata_pb2
@@ -399,6 +400,35 @@
   return device_names, fingerprints
 
 
+def GetZipEntryOffset(zfp, entry_info):
+  """Get offset to a beginning of a particular zip entry
+  Args:
+    fp: zipfile.ZipFile
+    entry_info: zipfile.ZipInfo
+
+  Returns:
+    (offset, size) tuple
+  """
+  # Don't use len(entry_info.extra). Because that returns size of extra
+  # fields in central directory. We need to look at local file directory,
+  # as these two might have different sizes.
+
+  # We cannot work with zipfile.ZipFile instances, we need a |fp| for the underlying file.
+  zfp = zfp.fp
+  zfp.seek(entry_info.header_offset)
+  data = zfp.read(zipfile.sizeFileHeader)
+  fheader = struct.unpack(zipfile.structFileHeader, data)
+  # Last two fields of local file header are filename length and
+  # extra length
+  filename_len = fheader[-2]
+  extra_len = fheader[-1]
+  offset = entry_info.header_offset
+  offset += zipfile.sizeFileHeader
+  offset += filename_len + extra_len
+  size = entry_info.file_size
+  return (offset, size)
+
+
 class PropertyFiles(object):
   """A class that computes the property-files string for an OTA package.
 
@@ -517,10 +547,7 @@
     def ComputeEntryOffsetSize(name):
       """Computes the zip entry offset and size."""
       info = zip_file.getinfo(name)
-      offset = info.header_offset
-      offset += zipfile.sizeFileHeader
-      offset += len(info.extra) + len(info.filename)
-      size = info.file_size
+      (offset, size) = GetZipEntryOffset(zip_file, info)
       return '%s:%d:%d' % (os.path.basename(name), offset, size)
 
     tokens = []
@@ -569,3 +596,45 @@
 
   SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
            whole_file=True)
+
+
+def ConstructOtaApexInfo(target_zip, source_file=None):
+  """If applicable, add the source version to the apex info."""
+
+  def _ReadApexInfo(input_zip):
+    if "META/apex_info.pb" not in input_zip.namelist():
+      logger.warning("target_file doesn't contain apex_info.pb %s", input_zip)
+      return None
+
+    with input_zip.open("META/apex_info.pb", "r") as zfp:
+      return zfp.read()
+
+  target_apex_string = _ReadApexInfo(target_zip)
+  # Return early if the target apex info doesn't exist or is empty.
+  if not target_apex_string:
+    return target_apex_string
+
+  # If the source apex info isn't available, just return the target info
+  if not source_file:
+    return target_apex_string
+
+  with zipfile.ZipFile(source_file, "r", allowZip64=True) as source_zip:
+    source_apex_string = _ReadApexInfo(source_zip)
+  if not source_apex_string:
+    return target_apex_string
+
+  source_apex_proto = ota_metadata_pb2.ApexMetadata()
+  source_apex_proto.ParseFromString(source_apex_string)
+  source_apex_versions = {apex.package_name: apex.version for apex in
+                          source_apex_proto.apex_info}
+
+  # If the apex package is available in the source build, initialize the source
+  # apex version.
+  target_apex_proto = ota_metadata_pb2.ApexMetadata()
+  target_apex_proto.ParseFromString(target_apex_string)
+  for target_apex in target_apex_proto.apex_info:
+    name = target_apex.package_name
+    if name in source_apex_versions:
+      target_apex.source_version = source_apex_versions[name]
+
+  return target_apex_proto.SerializeToString()
diff --git a/tools/releasetools/sign_apex.py b/tools/releasetools/sign_apex.py
index fb947f4..679f57a 100755
--- a/tools/releasetools/sign_apex.py
+++ b/tools/releasetools/sign_apex.py
@@ -39,6 +39,9 @@
   --codename_to_api_level_map Q:29,R:30,...
       A Mapping of codename to api level.  This is useful to provide sdk targeting
       information to APK Signer.
+
+  --sign_tool <sign_tool>
+      Optional flag that specifies a custom signing tool for the contents of the apex.
 """
 
 import logging
@@ -52,7 +55,7 @@
 
 
 def SignApexFile(avbtool, apex_file, payload_key, container_key, no_hashtree,
-                 apk_keys=None, signing_args=None, codename_to_api_level_map=None):
+                 apk_keys=None, signing_args=None, codename_to_api_level_map=None, sign_tool=None):
   """Signs the given apex file."""
   with open(apex_file, 'rb') as input_fp:
     apex_data = input_fp.read()
@@ -66,7 +69,8 @@
       codename_to_api_level_map=codename_to_api_level_map,
       no_hashtree=no_hashtree,
       apk_keys=apk_keys,
-      signing_args=signing_args)
+      signing_args=signing_args,
+      sign_tool=sign_tool)
 
 
 def main(argv):
@@ -100,6 +104,8 @@
         if 'extra_apks' not in options:
           options['extra_apks'] = {}
         options['extra_apks'].update({n: key})
+    elif o == '--sign_tool':
+      options['sign_tool'] = a
     else:
       return False
     return True
@@ -114,6 +120,7 @@
           'payload_extra_args=',
           'payload_key=',
           'extra_apks=',
+          'sign_tool=',
       ],
       extra_option_handler=option_handler)
 
@@ -133,7 +140,8 @@
       apk_keys=options.get('extra_apks', {}),
       signing_args=options.get('payload_extra_args'),
       codename_to_api_level_map=options.get(
-          'codename_to_api_level_map', {}))
+          'codename_to_api_level_map', {}),
+      sign_tool=options['sign_tool'])
   shutil.copyfile(signed_apex, args[1])
   logger.info("done.")
 
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 0842af9..5626980 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -136,6 +136,11 @@
 
   --android_jar_path <path>
       Path to the android.jar to repack the apex file.
+
+  --allow_gsi_debug_sepolicy
+      Allow the existence of the file 'userdebug_plat_sepolicy.cil' under
+      (/system/system_ext|/system_ext)/etc/selinux.
+      If not set, error out when the file exists.
 """
 
 from __future__ import print_function
@@ -189,6 +194,9 @@
 OPTIONS.gki_signing_algorithm = None
 OPTIONS.gki_signing_extra_args = None
 OPTIONS.android_jar_path = None
+OPTIONS.vendor_partitions = set()
+OPTIONS.vendor_otatools = None
+OPTIONS.allow_gsi_debug_sepolicy = False
 
 
 AVB_FOOTER_ARGS_BY_PARTITION = {
@@ -216,6 +224,10 @@
   if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
     raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
 
+# Partitions that can be regenerated after signing using a separate
+# vendor otatools package.
+ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
+
 
 def IsApexFile(filename):
   return filename.endswith(".apex") or filename.endswith(".capex")
@@ -250,7 +262,7 @@
 
   Args:
     keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
-        container_key).
+        container_key, sign_tool).
     key_map: A dict that overrides the keys, specified via command-line input.
 
   Returns:
@@ -268,11 +280,11 @@
     if apex not in keys_info:
       logger.warning('Failed to find %s in target_files; Ignored', apex)
       continue
-    keys_info[apex] = (key, keys_info[apex][1])
+    keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
 
   # Apply the key remapping to container keys.
-  for apex, (payload_key, container_key) in keys_info.items():
-    keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
+  for apex, (payload_key, container_key, sign_tool) in keys_info.items():
+    keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
 
   # Apply all the --extra_apks options to override the container keys.
   for apex, key in OPTIONS.extra_apks.items():
@@ -281,13 +293,13 @@
       continue
     if not key:
       key = 'PRESIGNED'
-    keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
+    keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
 
   # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
   # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
   # (overridden via commandline) indicates a config error, which should not be
   # allowed.
-  for apex, (payload_key, container_key) in keys_info.items():
+  for apex, (payload_key, container_key, sign_tool) in keys_info.items():
     if container_key != 'PRESIGNED':
       continue
     if apex in OPTIONS.extra_apex_payload_keys:
@@ -299,7 +311,7 @@
       print(
           "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
               apex))
-    keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
+    keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
 
   return keys_info
 
@@ -360,7 +372,7 @@
     compressed_extension: The extension string of compressed APKs, such as
         '.gz', or None if there's no compressed APKs.
     apex_keys: A dict that contains the key mapping from APEX name to
-        (payload_key, container_key).
+        (payload_key, container_key, sign_tool).
 
   Raises:
     AssertionError: On finding unknown APKs and APEXes.
@@ -405,7 +417,7 @@
 
     name = GetApexFilename(info.filename)
 
-    (payload_key, container_key) = apex_keys[name]
+    (payload_key, container_key, _) = apex_keys[name]
     if ((payload_key in common.SPECIAL_CERT_STRINGS and
          container_key not in common.SPECIAL_CERT_STRINGS) or
         (payload_key not in common.SPECIAL_CERT_STRINGS and
@@ -557,7 +569,7 @@
     elif IsApexFile(filename):
       name = GetApexFilename(filename)
 
-      payload_key, container_key = apex_keys[name]
+      payload_key, container_key, sign_tool = apex_keys[name]
 
       # We've asserted not having a case with only one of them PRESIGNED.
       if (payload_key not in common.SPECIAL_CERT_STRINGS and
@@ -576,7 +588,8 @@
             apk_keys,
             codename_to_api_level_map,
             no_hashtree=None,  # Let apex_util determine if hash tree is needed
-            signing_args=OPTIONS.avb_extra_args.get('apex'))
+            signing_args=OPTIONS.avb_extra_args.get('apex'),
+            sign_tool=sign_tool)
         common.ZipWrite(output_tf_zip, signed_apex, filename)
 
       else:
@@ -658,7 +671,7 @@
     # Updates system_other.avbpubkey in /product/etc/.
     elif filename in (
         "PRODUCT/etc/security/avb/system_other.avbpubkey",
-            "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
+        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
       # Only update system_other's public key, if the corresponding signing
       # key is specified via --avb_system_other_key.
       signing_key = OPTIONS.avb_keys.get("system_other")
@@ -671,9 +684,19 @@
     # Should NOT sign boot-debug.img.
     elif filename in (
         "BOOT/RAMDISK/force_debuggable",
-            "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
+        "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
       raise common.ExternalError("debuggable boot.img cannot be signed")
 
+    # Should NOT sign userdebug sepolicy file.
+    elif filename in (
+        "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
+        "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
+      if not OPTIONS.allow_gsi_debug_sepolicy:
+        raise common.ExternalError("debug sepolicy shouldn't be included")
+      else:
+        # Copy it verbatim if we allow the file to exist.
+        common.ZipWriteStr(output_tf_zip, out_info, data)
+
     # A non-APK file; copy it verbatim.
     else:
       common.ZipWriteStr(output_tf_zip, out_info, data)
@@ -1125,15 +1148,16 @@
 
   Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
   dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
-  tuple of (payload_key, container_key).
+  tuple of (payload_key, container_key, sign_tool).
 
   Args:
     tf_zip: The input target_files ZipFile (already open).
 
   Returns:
-    (payload_key, container_key): payload_key contains the path to the payload
-        signing key; container_key contains the path to the container signing
-        key.
+    (payload_key, container_key, sign_tool):
+      - payload_key contains the path to the payload signing key
+      - container_key contains the path to the container signing key
+      - sign_tool is an apex-specific signing tool for its payload contents
   """
   keys = {}
   for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
@@ -1146,7 +1170,8 @@
         r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
         r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
         r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
-        r'(\s+partition="(?P<PARTITION>.*?)")?$',
+        r'(\s+partition="(?P<PARTITION>.*?)")?'
+        r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
         line)
     if not matches:
       continue
@@ -1175,11 +1200,69 @@
     else:
       raise ValueError("Failed to parse container keys: \n{}".format(line))
 
-    keys[name] = (payload_private_key, container_key)
+    sign_tool = matches.group("SIGN_TOOL")
+    keys[name] = (payload_private_key, container_key, sign_tool)
 
   return keys
 
 
+def BuildVendorPartitions(output_zip_path):
+  """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
+  if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
+    logger.warning("Allowed --vendor_partitions: %s",
+                   ",".join(ALLOWED_VENDOR_PARTITIONS))
+    OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
+        OPTIONS.vendor_partitions)
+
+  logger.info("Building vendor partitions using vendor otatools.")
+  vendor_tempdir = common.UnzipTemp(output_zip_path, [
+      "META/*",
+  ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
+
+  # Disable various partitions that build based on misc_info fields.
+  # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
+  # vendor otatools. These other partitions will be rebuilt using the main
+  # otatools if necessary.
+  vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
+  vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
+  vendor_misc_info["no_boot"] = "true"  # boot
+  vendor_misc_info["vendor_boot"] = "false"  # vendor_boot
+  vendor_misc_info["no_recovery"] = "true"  # recovery
+  vendor_misc_info["board_bpt_enable"] = "false"  # partition-table
+  vendor_misc_info["has_dtbo"] = "false"  # dtbo
+  vendor_misc_info["has_pvmfw"] = "false"  # pvmfw
+  vendor_misc_info["avb_custom_images_partition_list"] = ""  # custom images
+  vendor_misc_info["avb_enable"] = "false"  # vbmeta
+  vendor_misc_info["use_dynamic_partitions"] = "false"  # super_empty
+  vendor_misc_info["build_super_partition"] = "false"  # super split
+  with open(vendor_misc_info_path, "w") as output:
+    for key in sorted(vendor_misc_info):
+      output.write("{}={}\n".format(key, vendor_misc_info[key]))
+
+  # Disable care_map.pb as not all ab_partitions are available when
+  # vendor otatools regenerates vendor images.
+  os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
+
+  # Build vendor images using vendor otatools.
+  vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
+  common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
+  cmd = [
+      os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
+      "--is_signing",
+      "--verbose",
+      vendor_tempdir,
+  ]
+  common.RunAndCheckOutput(cmd, verbose=True)
+
+  logger.info("Writing vendor partitions to output archive.")
+  with zipfile.ZipFile(
+      output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
+      allowZip64=True) as output_zip:
+    for p in OPTIONS.vendor_partitions:
+      path = "IMAGES/{}.img".format(p)
+      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, path), path)
+
+
 def main(argv):
 
   key_mapping_options = []
@@ -1289,6 +1372,12 @@
       OPTIONS.gki_signing_algorithm = a
     elif o == "--gki_signing_extra_args":
       OPTIONS.gki_signing_extra_args = a
+    elif o == "--vendor_otatools":
+      OPTIONS.vendor_otatools = a
+    elif o == "--vendor_partitions":
+      OPTIONS.vendor_partitions = set(a.split(","))
+    elif o == "--allow_gsi_debug_sepolicy":
+      OPTIONS.allow_gsi_debug_sepolicy = True
     else:
       return False
     return True
@@ -1339,6 +1428,9 @@
           "gki_signing_key=",
           "gki_signing_algorithm=",
           "gki_signing_extra_args=",
+          "vendor_partitions=",
+          "vendor_otatools=",
+          "allow_gsi_debug_sepolicy",
       ],
       extra_option_handler=option_handler)
 
@@ -1384,8 +1476,11 @@
   common.ZipClose(input_zip)
   common.ZipClose(output_zip)
 
+  if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
+    BuildVendorPartitions(args[1])
+
   # Skip building userdata.img and cache.img when signing the target files.
-  new_args = ["--is_signing"]
+  new_args = ["--is_signing", "--add_missing", "--verbose"]
   # add_img_to_target_files builds the system image from scratch, so the
   # recovery patch is guaranteed to be regenerated there.
   if OPTIONS.rebuild_recovery:
diff --git a/tools/releasetools/test_apex_utils.py b/tools/releasetools/test_apex_utils.py
index 71f6433..ed920f2 100644
--- a/tools/releasetools/test_apex_utils.py
+++ b/tools/releasetools/test_apex_utils.py
@@ -187,3 +187,19 @@
 
     self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
     signer.ProcessApexFile(apk_keys, self.payload_key)
+
+  @test_utils.SkipIfExternalToolsUnavailable()
+  def test_ApexApkSigner_invokesCustomSignTool(self):
+    apex_path = common.MakeTempFile(suffix='.apex')
+    shutil.copy(self.apex_with_apk, apex_path)
+    apk_keys = {'wifi-service-resources.apk': os.path.join(
+        self.testdata_dir, 'testkey')}
+    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
+
+    # pass `false` as a sign_tool to see the invocation error
+    with self.assertRaises(common.ExternalError) as cm:
+        signer = apex_utils.ApexApkSigner(apex_path, None, None, sign_tool='false')
+        signer.ProcessApexFile(apk_keys, self.payload_key)
+
+    the_exception = cm.exception
+    self.assertIn('Failed to run command \'[\'false\'', the_exception.message)
diff --git a/tools/releasetools/test_build_image.py b/tools/releasetools/test_build_image.py
index b24805f..cfae7a5 100644
--- a/tools/releasetools/test_build_image.py
+++ b/tools/releasetools/test_build_image.py
@@ -196,7 +196,7 @@
     p.communicate()
     self.assertEqual(0, p.returncode)
 
-    fs_dict = GetFilesystemCharacteristics(output_file)
+    fs_dict = GetFilesystemCharacteristics('ext4', output_file)
     self.assertEqual(int(fs_dict['Block size']), 4096)
     self.assertGreaterEqual(int(fs_dict['Free blocks']), 0) # expect ~88
     self.assertGreater(int(fs_dict['Inode count']), 0)      # expect ~64
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 1a00549..e42d417 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1631,88 +1631,6 @@
     self.assertEqual('3', chained_partition_args[1])
     self.assertTrue(os.path.exists(chained_partition_args[2]))
 
-  def test_BuildVBMeta_appendAftlCommandSyntax(self):
-    testdata_dir = test_utils.get_testdata_dir()
-    common.OPTIONS.info_dict = {
-        'ab_update': 'true',
-        'avb_avbtool': 'avbtool',
-        'build.prop': common.PartitionBuildProps.FromDictionary(
-            'system', {
-                'ro.build.version.incremental': '6285659',
-                'ro.product.device': 'coral',
-                'ro.build.fingerprint':
-                'google/coral/coral:R/RP1A.200311.002/'
-                '6285659:userdebug/dev-keys'}
-        ),
-    }
-    common.OPTIONS.aftl_tool_path = 'aftltool'
-    common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
-    common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
-                                                'test_transparency_key.pub')
-    common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
-        testdata_dir, 'test_aftl_rsa4096.pem')
-
-    vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
-    cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
-    expected_cmd = [
-        'aftltool', 'make_icp_from_vbmeta',
-        '--vbmeta_image_path', 'place_holder',
-        '--output', vbmeta_image.name,
-        '--version_incremental', '6285659',
-        '--transparency_log_servers',
-        'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
-            common.OPTIONS.aftl_key_path),
-        '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
-        '--algorithm', 'SHA256_RSA4096',
-        '--padding', '4096']
-
-    # ignore the place holder, i.e. path to a temp file
-    self.assertEqual(cmd[:3], expected_cmd[:3])
-    self.assertEqual(cmd[4:], expected_cmd[4:])
-
-  @unittest.skip("enable after we have a server for public")
-  def test_BuildVBMeta_appendAftlContactServer(self):
-    testdata_dir = test_utils.get_testdata_dir()
-    common.OPTIONS.info_dict = {
-        'ab_update': 'true',
-        'avb_avbtool': 'avbtool',
-        'build.prop': common.PartitionBuildProps.FromDictionary(
-            'system', {
-                'ro.build.version.incremental': '6285659',
-                'ro.product.device': 'coral',
-                'ro.build.fingerprint':
-                'google/coral/coral:R/RP1A.200311.002/'
-                '6285659:userdebug/dev-keys'}
-        )
-    }
-    common.OPTIONS.aftl_tool_path = "aftltool"
-    common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
-    common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
-                                                'test_transparency_key.pub')
-    common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
-        testdata_dir, 'test_aftl_rsa4096.pem')
-
-    input_dir = common.MakeTempDir()
-    system_image = common.MakeTempFile()
-    build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
-                       '/system', str(4096 * 100), '-j', '0', '-s']
-    common.RunAndCheckOutput(build_image_cmd)
-
-    add_footer_cmd = ['avbtool', 'add_hashtree_footer',
-                      '--partition_size', str(4096 * 150),
-                      '--partition_name', 'system',
-                      '--image', system_image]
-    common.RunAndCheckOutput(add_footer_cmd)
-
-    vbmeta_image = common.MakeTempFile()
-    common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
-                       ['system'])
-
-    verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
-                  vbmeta_image, '--transparency_log_pub_keys',
-                  common.OPTIONS.aftl_key_path]
-    common.RunAndCheckOutput(verify_cmd)
-
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_AppendGkiSigningArgs_NoSigningKeyPath(self):
     # A non-GKI boot.img has no gki_signing_key_path.
diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py
index 4f61472..835edab 100644
--- a/tools/releasetools/test_merge_target_files.py
+++ b/tools/releasetools/test_merge_target_files.py
@@ -265,10 +265,10 @@
         'system': 'system',
         'product': 'product',
         'vendor': 'vendor',
-    }, os.path.join(product_out_dir, 'policy'))
+    })
     self.assertEqual(' '.join(cmd),
                      ('secilc -m -M true -G -N -c 30 '
-                      '-o {OTP}/policy -f /dev/null '
+                      '-o {OTP}/META/combined_sepolicy -f /dev/null '
                       '{OTP}/system/etc/selinux/plat_sepolicy.cil '
                       '{OTP}/system/etc/selinux/mapping/30.0.cil '
                       '{OTP}/vendor/etc/selinux/vendor_sepolicy.cil '
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index 661712a..11cfee1 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -24,7 +24,7 @@
 import test_utils
 from ota_utils import (
     BuildLegacyOtaMetadata, CalculateRuntimeDevicesAndFingerprints,
-    FinalizeMetadata, GetPackageMetadata, PropertyFiles)
+    ConstructOtaApexInfo, FinalizeMetadata, GetPackageMetadata, PropertyFiles)
 from ota_from_target_files import (
     _LoadOemDicts, AbOtaPropertyFiles,
     GetTargetFilesZipForCustomImagesUpdates,
@@ -295,6 +295,35 @@
     uncompressed_apex_size = os.path.getsize(original_apex_filepath)
     self.assertEqual(apex_infos[0].decompressed_size, uncompressed_apex_size)
 
+  @staticmethod
+  def construct_tf_with_apex_info(infos):
+    apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
+    apex_metadata_proto.apex_info.extend(infos)
+
+    output = common.MakeTempFile(suffix='.zip')
+    with zipfile.ZipFile(output, 'w') as zfp:
+      common.ZipWriteStr(zfp, "META/apex_info.pb",
+                         apex_metadata_proto.SerializeToString())
+    return output
+
+  def test_ConstructOtaApexInfo_incremental_package(self):
+    infos = [ota_metadata_pb2.ApexInfo(package_name='com.android.apex.1',
+                                       version=1000, is_compressed=False),
+             ota_metadata_pb2.ApexInfo(package_name='com.android.apex.2',
+                                       version=2000, is_compressed=True)]
+    target_file = self.construct_tf_with_apex_info(infos)
+
+    with zipfile.ZipFile(target_file) as target_zip:
+      info_bytes = ConstructOtaApexInfo(target_zip, source_file=target_file)
+    apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
+    apex_metadata_proto.ParseFromString(info_bytes)
+
+    info_list = apex_metadata_proto.apex_info
+    self.assertEqual(2, len(info_list))
+    self.assertEqual('com.android.apex.1', info_list[0].package_name)
+    self.assertEqual(1000, info_list[0].version)
+    self.assertEqual(1000, info_list[0].source_version)
+
   def test_GetPackageMetadata_retrofitDynamicPartitions(self):
     target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
     common.OPTIONS.retrofit_dynamic_partitions = True
@@ -834,6 +863,7 @@
         property_files.required)
     self.assertEqual(
         (
+            'apex_info.pb',
             'care_map.pb',
             'care_map.txt',
             'compatibility.zip',
@@ -933,6 +963,7 @@
         property_files.required)
     self.assertEqual(
         (
+            'apex_info.pb',
             'care_map.pb',
             'care_map.txt',
             'compatibility.zip',
diff --git a/tools/releasetools/test_ota_utils.py b/tools/releasetools/test_ota_utils.py
new file mode 100644
index 0000000..9a82e6f
--- /dev/null
+++ b/tools/releasetools/test_ota_utils.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2021 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.
+
+
+import unittest
+import io
+import ota_utils
+import zipfile
+
+
+class TestZipEntryOffset(unittest.TestCase):
+  def test_extra_length_differ(self):
+      # This is a magic zip file such that:
+      # 1. It has 1 entry, `file.txt'`
+      # 2. The central directory entry for the entry contains an extra field of#
+      # length 24, while the local file header for the entry contains an extra#
+      # field of length 28.
+      # It is key that the entry contains extra field of different length.
+      # The sole purpose of this test case is make sure our offset computing
+      # logic works in this scenario.
+
+      # This is created by:
+      # touch file.txt
+      # zip -0 test.zip file.txt
+      # Above command may or may not work on all platforms.
+      # Some zip implementation will keep the extra field size consistent.
+      # Some don't
+    magic_zip = b'PK\x03\x04\n\x00\x00\x00\x00\x00nY\xfcR\x00\x00\x00\x00\x00\x00\x00' +\
+        b'\x00\x00\x00\x00\x00\x08\x00\x1c\x00file.txtUT\t\x00\x03' +\
+        b'\xa0s\x01a\xa0s\x01aux\x0b\x00\x01\x04\x88\xc4\t\x00\x04S_\x01\x00' +\
+        b'PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00nY\xfcR\x00\x00\x00\x00' +\
+        b'\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x18\x00\x00\x00\x00\x00' +\
+        b'\x00\x00\x00\x00\x80\x81\x00\x00\x00\x00file.txt' +\
+        b'UT\x05\x00\x03\xa0s\x01aux\x0b\x00\x01\x04\x88\xc4\t\x00\x04' +\
+        b'S_\x01\x00PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00N\x00\x00' +\
+        b'\x00B\x00\x00\x00\x00\x00'
+    # Just making sure we concatenated the bytes correctly
+    self.assertEqual(len(magic_zip), 166)
+    fp = io.BytesIO(magic_zip)
+    with zipfile.ZipFile(fp, 'r') as zfp:
+      self.assertGreater(len(zfp.infolist()), 0)
+      zinfo = zfp.getinfo("file.txt")
+      (offset, size) = ota_utils.GetZipEntryOffset(zfp, zinfo)
+      self.assertEqual(size, zinfo.file_size)
+      self.assertEqual(offset, zipfile.sizeFileHeader+len(zinfo.filename) + 28)
diff --git a/tools/releasetools/test_sign_apex.py b/tools/releasetools/test_sign_apex.py
index 646b04d..8470f20 100644
--- a/tools/releasetools/test_sign_apex.py
+++ b/tools/releasetools/test_sign_apex.py
@@ -69,5 +69,5 @@
         payload_key,
         container_key,
         False,
-        codename_to_api_level_map={'S': 31})
+        codename_to_api_level_map={'S': 31, 'Tiramisu' : 32})
     self.assertTrue(os.path.exists(signed_apex))
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index ad9e657..92dca9a 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -328,23 +328,23 @@
         'Apex3.apex' : 'key3',
     }
     apex_keys = {
-        'Apex1.apex' : ('payload-key1', 'container-key1'),
-        'Apex2.apex' : ('payload-key2', 'container-key2'),
+        'Apex1.apex' : ('payload-key1', 'container-key1', None),
+        'Apex2.apex' : ('payload-key2', 'container-key2', None),
     }
     with zipfile.ZipFile(input_file) as input_zip:
       CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
 
       # Fine to have both keys as PRESIGNED.
-      apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED')
+      apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED', None)
       CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
 
       # Having only one of them as PRESIGNED is not allowed.
-      apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED')
+      apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED', None)
       self.assertRaises(
           AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
           None, apex_keys)
 
-      apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1')
+      apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1', None)
       self.assertRaises(
           AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
           None, apex_keys)
@@ -475,10 +475,10 @@
     self.assertEqual({
         'apex.apexd_test.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         'apex.apexd_test_different_app.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         }, keys_info)
 
   def test_ReadApexKeysInfo_mismatchingContainerKeys(self):
@@ -514,10 +514,10 @@
     self.assertEqual({
         'apex.apexd_test.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         'apex.apexd_test_different_app.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         }, keys_info)
 
   def test_ReadApexKeysInfo_missingPayloadPublicKey(self):
@@ -537,10 +537,10 @@
     self.assertEqual({
         'apex.apexd_test.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         'apex.apexd_test_different_app.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         }, keys_info)
 
   def test_ReadApexKeysInfo_presignedKeys(self):
@@ -560,10 +560,10 @@
     self.assertEqual({
         'apex.apexd_test.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         'apex.apexd_test_different_app.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         }, keys_info)
 
   def test_ReadApexKeysInfo_presignedKeys(self):
@@ -583,10 +583,10 @@
     self.assertEqual({
         'apex.apexd_test.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         'apex.apexd_test_different_app.apex': (
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
-            'build/make/target/product/security/testkey'),
+            'build/make/target/product/security/testkey', None),
         }, keys_info)
 
   def test_ReplaceGkiSigningKey(self):
diff --git a/tools/releasetools/testdata/test_aftl_rsa4096.pem b/tools/releasetools/testdata/test_aftl_rsa4096.pem
deleted file mode 100644
index 89f1ef3..0000000
--- a/tools/releasetools/testdata/test_aftl_rsa4096.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDDlhUPUgtWL6LB
-Wybp6wsEJeioV1aRLPGSA2/xIpTiJUK46cb/MD5eBTWjKENoIgX23eL/ePy2I68e
-+WvcZ5ITGOTRQqNVZIdc5qvr03wkV0BsJQMHSMAHacePpB/4xM5MzN/6Ku1wA8Dw
-uK+v/Cw4hqq8H/gP0oPVQ1bwcIePzRPX4YkkyXusoyzTIm5DJ9reVtyFucKqANCN
-aFmGxcaEc2nADtARQWJpO95joFsMvr68+JBxpCt8aWbxuSz/rLJ9Y8Z46V/++XG+
-E4QEob/WVY5pUD/RyogLrfhIf+zO7R3wJklXElSFacIX9+RzR9dgkQVbqxLfBKIP
-XWLCsF4I4EnvqUtaVjIMl8UpZpoq8pDLRqZ71Os5xZYq06x9E02M6DnvFbZEdaOX
-MCz2mmNX3g5FahvJayBhCuNhyTkd79MFR71Wp48TvWxKz3S7q0T0cWHNhtPkHSCa
-KwD93AQnqtLKYDGkHIZBzJPcs+QxbzdHyGzhXZb+qh5KmQvNA9HRBQY1RkMmzIbI
-8pzYTwpOkbCEhVoCWcRaaF1Pgl+zcpgJOMbBBUabx/dConFIhMDW/I5fHgKgwGqm
-tWUibrMPdnfS6W5MXi8jC0eDuZl0VwmdE+4dLujiOofUYnb7D+GXojf3PrSLcTw1
-PmG0f7l5xDKN9a0N+IXqvD2oAANTsQIDAQABAoICAQCW5HXw8OogHvYg2HMIKrbA
-B4McRO1baWIhtRcq4PQeGIMGaA2HmS+0l65O5uRCNWWGlJ7pW+0TlCop6mHFk/4F
-T8JQk2mxmrI4ARqIAQwYeVwRUuioOP81eO1mK0gjQ6qpY7I0reOq9KpozQN18UYo
-gfS82Kkng9EDukUbkKV1UtFJTw3gXLVWdjlB1qFcnCXmPPs7DBpbz+8V+XiAWpsS
-WnwumP77IQeMiozDLdaw2YQMBHRjyDVocWTjfmpyAkleJZjcdagC7W1MKIBElomL
-EUyigTALaYZWBGy1ekQ3TIY5XUBdtZ2RpAsDNNOCAN3v+VI565zOhCOHWRO1gh24
-vyhBFR0HYqBRoLbLAqo8bM5iLPz1EWGyaTnfxt38J8Va0TD7KihcBnphiA+dkhEF
-oc0yIp/8S2o3CfkNok7Ju8Amb7M4JJuKhuP8wxn86fAHpjjd3Y4SlZp0NrTrd7T2
-msLIneb1OUZZxFxyJG1XQGEZplLPalnGadIF4p3q/3nd1rVb491qCNl/A5QwhI9r
-ZV62O90M9fu3+cAynBLbMT09IZecNwP1gXmunlY6YH+ymM+3NFqC8q2tnzomiz8/
-Fee0ftZ2C/jK62fET0Y8LPWGkVQGHtvZH0FPg4suA0GMmYAe0tQl93A+jFltfKKZ
-RgCDrYs6Wv76E9gnWVnEdQKCAQEA8L76LjZUTKOg83Bra+hP+cXnwGsgwOwJfGBp
-OM++5HzlpYjtbD38esBZVJtwb/8xJGdsHtP2n7ZgbSDuAnRj5S50QHIApvRkz1Y+
-1hL8tAdgVP2JkYjpyG3bPk4QVKyXkKvBcp2BCidXs75+HzfOxqkazumaYOYo2guh
-azHdka2xSqxcZqo4yyORc/oue25RU4skmuNDOlP0+OTxU/uXnl7QZmlaOfT5TqO4
-s7uER4BXt/87j44mnOBdXmtqrsL49+R9bzVskx76aeuaBbwf7jnpR058E71OZwSd
-F1P3fx6hl0yLOZF/5Jnq+14rEna6jH50XtzlhB6deSZFTOw2gwKCAQEAz/qXRzwH
-I0YWISgkUG2zBJseHmfHqV4CDzb5+tTJ3B2I8cXE0m2sQJXi2s7oMhWSc1cQOHCX
-txpgWaD59uBz2lcwnGRNp27TRXv8Wo+X0+O+lGWU2cO+j8AB2Vtb7F7rCySp0+Uu
-z+dBfoQ2zhKEQlkX0YldVILGzCL3QBHVvPC4iDlwkMRbcejDoh9NsBtHL8lG+MAw
-ZXbwJjhaJkhTXJFpJpejq70naS8VVlLt8Os80iuBXe5JK/ecAHtsNcJlXO02sMNZ
-Fbcy8WosGyvRKQ/tHtTjAlxZ7Ey8usWE8BvWBdUgiIBkIcjLtE2GrA8eOGNb3v1I
-HRt8NsV8yaLWuwKCAQAR7SaT6le8nTKO7gARuOq7npDzMwbtVqYeLM+o+08rlGFF
-QjzronH6cfg05J4quMXgABN8+CuVGO91MM6IQEJv/lWJtvN1ex1GkxV6u0812JbD
-vV1RCPDfi86XhRiSNYfTrfZponDJYMSXDcg2auFqyYzFe3+TV5ATLGqIoN3uyxA4
-jz0SJ/qypaNfD3IGnuBPaD0Bi4ql/TpwjhuqNUHE+SprdczSI/usb2SBfaUL7fKa
-MNcuiVc2tz48maMIAFypmMn+TewXyGa9HF4Lr0ZxZr6IIL/8eEwuP5my8v2q6Yz+
-xyRW1Q7A5vUoYoqyhUS+0Wu45JnyjJUNQFxIrg4hAoIBAF1uBIGSvN4iwRQ6FT4w
-WahrCre8BVzXh3NQTjJZXylL91YtcwLZE/Wbn+KN6o99U2IPLZE9O1qdNcVt5Hz8
-Te87FfJbuOrLhYuEbFQ+h4U/nUDK9XhyT+wB5JLBUOU5qrtByC0Rmtr411o/iONA
-PDwWC/YskEnDygywdIRKvsr3FN7VdvUB0Na2KxRsnZjMWElmUUS0Ccm7CZ0R2aWy
-/gfqpuMYYgVnnwnIhfxWmt+MvbDorGAHCMYAoQsyZuUrpB9/zP7RcvanavI6sP+v
-ynF43xvnpOdNl3Po8SuyScsXpijOmqPXkaP/sUsZPLOUww2vzPi6raetzjpIs4td
-ZLsCggEAe42Zj3FEbruJZeDgmd9lSc0j8UF90mNw8KH44IbuA6R9fGv3WkrNHEVd
-XZOwjWqAxhOj6pFoJk8n6h5d8iS/yXFZ0AfBMc21XMecu9mnfx9E9LFAIWmv7Wut
-vy3h2BqY+crglpg5RAw+3J97HAGMYCvp+hH2il+9zzjpmCtTD21LRMkw34szY7RR
-CDy9G5FTmKVlxw5eegvyj164olQRLurEdUIfSr5UnBjrWftJHy9JW8KWCeFDSmm9
-xCl3nGDyQuZmOTngxPtrOYAhb5LoKR9BeGcy6jlom7V4nYYqm3t1IDBgMqjYGT9c
-vqQgxO2OFsQOJQ/4PRYEKd1neTlZrw==
------END PRIVATE KEY-----
diff --git a/tools/releasetools/testdata/test_transparency_key.pub b/tools/releasetools/testdata/test_transparency_key.pub
deleted file mode 100644
index 8bfd816..0000000
--- a/tools/releasetools/testdata/test_transparency_key.pub
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4ilqCNsenNA013iCdwgD
-YPxZ853nbHG9lMBp9boXiwRcqT/8bUKHIL7YX5z7s+QoRYVY3rkMKppRabclXzyx
-H59YnPMaU4uv7NqwWzjgaZo7E+vo7IF+KBjV3cJulId5Av0yIYUCsrwd7MpGtWdC
-Q3S+7Vd4zwzCKEhcvliNIhnNlp1U3wNkPCxOyCAsMEn6k8O5ar12ke5TvxDv15db
-rPDeHh8G2OYWoCkWL+lSN35L2kOJqKqVbLKWrrOd96RCYrrtbPCi580OADJRcUlG
-lgcjwmNwmypBWvQMZ6ITj0P0ksHnl1zZz1DE2rXe1goLI1doghb5KxLaezlR8c2C
-E3w/uo9KJgNmNgUVzzqZZ6FE0moyIDNOpP7KtZAL0DvEZj6jqLbB0ccPQElrg52m
-Dv2/A3nYSr0mYBKeskT4+Bg7PGgoC8p7WyLSxMyzJEDYdtrj9OFx6eZaA23oqTQx
-k3Qq5H8RfNBeeSUEeKF7pKH/7gyqZ2bNzBFMA2EBZgBozwRfaeN/HCv3qbaCnwvu
-6caacmAsK+RxiYxSL1QsJqyhCWWGxVyenmxdc1KG/u5ypi7OIioztyzR3t2tAzD3
-Nb+2t8lgHBRxbV24yiPlnvPmB1ZYEctXnlRR9Evpl1o9xA9NnybPHKr9rozN39CZ
-V/USB8K6ao1y5xPZxa8CZksCAwEAAQ==
------END PUBLIC KEY-----
-
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 401857f..622e57f 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -194,7 +194,8 @@
 
     # Check we have the same recovery target in the check and flash commands.
     assert check_partition == flash_partition, \
-        "Mismatching targets: {} vs {}".format(check_partition, flash_partition)
+        "Mismatching targets: {} vs {}".format(
+            check_partition, flash_partition)
 
     # Validate the SHA-1 of the recovery image.
     recovery_sha1 = flash_partition.split(':')[3]
@@ -248,6 +249,39 @@
     os.symlink(os.path.join(src, filename), os.path.join(dst, filename))
 
 
+def ValidatePartitionFingerprints(input_tmp, info_dict):
+  build_info = common.BuildInfo(info_dict)
+  if not build_info.avb_enabled:
+    logging.info("AVB not enabled, skipping partition fingerprint checks")
+    return
+  # Expected format:
+  #  Prop: com.android.build.vendor.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'
+  #  Prop: com.android.build.vendor_boot.fingerprint -> 'generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7335886:userdebug/test-keys'
+  p = re.compile(
+      r"Prop: com.android.build.(?P<partition>\w+).fingerprint -> '(?P<fingerprint>[\w\/:\.-]+)'")
+  for vbmeta_partition in ["vbmeta", "vbmeta_system"]:
+    image = os.path.join(input_tmp, "IMAGES", vbmeta_partition + ".img")
+    if not os.path.exists(image):
+      assert vbmeta_partition != "vbmeta",\
+          "{} is a required partition for AVB.".format(
+              vbmeta_partition)
+      logging.info("vb partition %s not present, skipping", vbmeta_partition)
+      continue
+
+    output = common.RunAndCheckOutput(
+        [info_dict["avb_avbtool"], "info_image", "--image", image])
+    matches = p.findall(output)
+    for (partition, fingerprint) in matches:
+      actual_fingerprint = build_info.GetPartitionFingerprint(
+          partition)
+      if actual_fingerprint is None:
+        logging.warning(
+            "Failed to get fingerprint for partition %s", partition)
+        continue
+      assert fingerprint == actual_fingerprint, "Fingerprint mismatch for partition {}, expected: {} actual: {}".format(
+          partition, fingerprint, actual_fingerprint)
+
+
 def ValidateVerifiedBootImages(input_tmp, info_dict, options):
   """Validates the Verified Boot related images.
 
@@ -273,7 +307,7 @@
   # longer copied from RADIO to the IMAGES folder. But avbtool assumes that
   # images are in IMAGES folder. So we symlink them.
   symlinkIfNotExists(os.path.join(input_tmp, "RADIO"),
-                    os.path.join(input_tmp, "IMAGES"))
+                     os.path.join(input_tmp, "IMAGES"))
   # Verified boot 1.0 (images signed with boot_signer and verity_signer).
   if info_dict.get('boot_signer') == 'true':
     logging.info('Verifying Verified Boot images...')
@@ -325,11 +359,12 @@
     if info_dict.get("system_root_image") != "true":
       verity_key_ramdisk = os.path.join(
           input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
-      assert os.path.exists(verity_key_ramdisk), 'Missing verity_key in ramdisk'
+      assert os.path.exists(
+          verity_key_ramdisk), 'Missing verity_key in ramdisk'
 
       assert filecmp.cmp(
           verity_key_mincrypt, verity_key_ramdisk, shallow=False), \
-              'Mismatching verity_key files in root and ramdisk'
+          'Mismatching verity_key files in root and ramdisk'
       logging.info('Verified the content of /verity_key in ramdisk')
 
     # Then verify the verity signed system/vendor/product images, against the
@@ -362,6 +397,8 @@
     if key is None:
       key = info_dict['avb_vbmeta_key_path']
 
+    ValidatePartitionFingerprints(input_tmp, info_dict)
+
     # avbtool verifies all the images that have descriptors listed in vbmeta.
     # Using `--follow_chain_partitions` so it would additionally verify chained
     # vbmeta partitions (e.g. vbmeta_system).
@@ -411,7 +448,7 @@
 
     # avbtool verifies recovery image for non-A/B devices.
     if (info_dict.get('ab_update') != 'true' and
-        info_dict.get('no_recovery') != 'true'):
+            info_dict.get('no_recovery') != 'true'):
       image = os.path.join(input_tmp, 'IMAGES', 'recovery.img')
       key = info_dict['avb_recovery_key_path']
       cmd = [info_dict['avb_avbtool'], 'verify_image', '--image', image,
@@ -427,21 +464,21 @@
 
 
 def CheckDataInconsistency(lines):
-    build_prop = {}
-    for line in lines:
-      if line.startswith("import") or line.startswith("#"):
-        continue
-      if "=" not in line:
-        continue
+  build_prop = {}
+  for line in lines:
+    if line.startswith("import") or line.startswith("#"):
+      continue
+    if "=" not in line:
+      continue
 
-      key, value = line.rstrip().split("=", 1)
-      if key in build_prop:
-        logging.info("Duplicated key found for {}".format(key))
-        if value != build_prop[key]:
-          logging.error("Key {} is defined twice with different values {} vs {}"
-                        .format(key, value, build_prop[key]))
-          return key
-      build_prop[key] = value
+    key, value = line.rstrip().split("=", 1)
+    if key in build_prop:
+      logging.info("Duplicated key found for {}".format(key))
+      if value != build_prop[key]:
+        logging.error("Key {} is defined twice with different values {} vs {}"
+                      .format(key, value, build_prop[key]))
+        return key
+    build_prop[key] = value
 
 
 def CheckBuildPropDuplicity(input_tmp):
diff --git a/tools/signapk/OWNERS b/tools/signapk/OWNERS
index 0b8d398..23cab0b 100644
--- a/tools/signapk/OWNERS
+++ b/tools/signapk/OWNERS
@@ -1,2 +1,2 @@
 cbrubaker@google.com
-klyubin@google.com
+mpgroover@google.com
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index 7e5c8fc..8bf1005 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -1020,6 +1020,7 @@
     private static void usage() {
         System.err.println("Usage: signapk [-w] " +
                            "[-a <alignment>] " +
+                           "[--align-file-size] " +
                            "[-providerClass <className>] " +
                            "[--min-sdk-version <n>] " +
                            "[--disable-v2] " +
@@ -1044,6 +1045,7 @@
         boolean signWholeFile = false;
         String providerClass = null;
         int alignment = 4;
+        boolean alignFileSize = false;
         Integer minSdkVersionOverride = null;
         boolean signUsingApkSignatureSchemeV2 = true;
         boolean signUsingApkSignatureSchemeV4 = false;
@@ -1063,6 +1065,9 @@
             } else if ("-a".equals(args[argstart])) {
                 alignment = Integer.parseInt(args[++argstart]);
                 ++argstart;
+            } else if ("--align-file-size".equals(args[argstart])) {
+                alignFileSize = true;
+                ++argstart;
             } else if ("--min-sdk-version".equals(args[argstart])) {
                 String minSdkVersionString = args[++argstart];
                 try {
@@ -1206,12 +1211,22 @@
                     ByteBuffer[] outputChunks = new ByteBuffer[] {v1SignedApk};
 
                     ZipSections zipSections = findMainZipSections(v1SignedApk);
-                    ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =
-                            apkSigner.outputZipSections2(
-                                    DataSources.asDataSource(zipSections.beforeCentralDir),
-                                    DataSources.asDataSource(zipSections.centralDir),
-                                    DataSources.asDataSource(zipSections.eocd));
-                    if (addV2SignatureRequest != null) {
+
+                    ByteBuffer eocd = ByteBuffer.allocate(zipSections.eocd.remaining());
+                    eocd.put(zipSections.eocd);
+                    eocd.flip();
+                    eocd.order(ByteOrder.LITTLE_ENDIAN);
+                    // This loop is supposed to be iterated twice at most.
+                    // The second pass is to align the file size after amending EOCD comments
+                    // with assumption that re-generated signing block would be the same size.
+                    while (true) {
+                        ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =
+                                apkSigner.outputZipSections2(
+                                        DataSources.asDataSource(zipSections.beforeCentralDir),
+                                        DataSources.asDataSource(zipSections.centralDir),
+                                        DataSources.asDataSource(eocd));
+                        if (addV2SignatureRequest == null) break;
+
                         // Need to insert the returned APK Signing Block before ZIP Central
                         // Directory.
                         int padding = addV2SignatureRequest.getPaddingSizeBeforeApkSigningBlock();
@@ -1219,8 +1234,8 @@
                         // Because the APK Signing Block is inserted before the Central Directory,
                         // we need to adjust accordingly the offset of Central Directory inside the
                         // ZIP End of Central Directory (EoCD) record.
-                        ByteBuffer modifiedEocd = ByteBuffer.allocate(zipSections.eocd.remaining());
-                        modifiedEocd.put(zipSections.eocd);
+                        ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining());
+                        modifiedEocd.put(eocd);
                         modifiedEocd.flip();
                         modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
                         ApkUtils.setZipEocdCentralDirectoryOffset(
@@ -1235,6 +1250,32 @@
                                         zipSections.centralDir,
                                         modifiedEocd};
                         addV2SignatureRequest.done();
+
+                        // Exit the loop if we don't need to align the file size
+                        if (!alignFileSize || alignment < 2) {
+                            break;
+                        }
+
+                        // Calculate the file size
+                        eocd = modifiedEocd;
+                        int fileSize = 0;
+                        for (ByteBuffer buf : outputChunks) {
+                            fileSize += buf.remaining();
+                        }
+                        // Exit the loop because the file size is aligned.
+                        if (fileSize % alignment == 0) {
+                            break;
+                        }
+                        // Pad EOCD comment to align the file size.
+                        int commentLen = alignment - fileSize % alignment;
+                        modifiedEocd = ByteBuffer.allocate(eocd.remaining() + commentLen);
+                        modifiedEocd.put(eocd);
+                        modifiedEocd.rewind();
+                        modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
+                        ApkUtils.updateZipEocdCommentLen(modifiedEocd);
+                        // Since V2 signing block should cover modified EOCD,
+                        // re-iterate the loop with modified EOCD.
+                        eocd = modifiedEocd;
                     }
 
                     // This assumes outputChunks are array-backed. To avoid this assumption, the
diff --git a/tools/warn/cpp_warn_patterns.py b/tools/warn/cpp_warn_patterns.py
index 2fa9916..1e1aa43 100644
--- a/tools/warn/cpp_warn_patterns.py
+++ b/tools/warn/cpp_warn_patterns.py
@@ -91,6 +91,10 @@
          [r".*: warning: incompatible redeclaration of library function .+"]),
     high('Null passed as non-null argument',
          [r".*: warning: Null passed to a callee that requires a non-null"]),
+    medium('Unused command line argument',
+           [r".*: warning: argument unused during compilation: .+"]),
+    medium('Set but not used',
+           [r".*: warning: .* set but not used.*Wunused-but-set"]),
     medium('Unused parameter',
            [r".*: warning: unused parameter '.*'"]),
     medium('Unused function, variable, label, comparison, etc.',
@@ -166,6 +170,8 @@
            [r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"]),
     medium('Shift count greater than width of type',
            [r".*: warning: (left|right) shift count >= width of type"]),
+    medium('Shift operator precedence',
+           [r".*: warning: operator .* has lower precedence .+Wshift-op-parentheses.+"]),
     medium('extern &lt;foo&gt; is initialized',
            [r".*: warning: '.+' initialized and declared 'extern'",
             r".*: warning: 'extern' variable has an initializer"]),
@@ -239,6 +245,8 @@
            [r".*: warning: ignoring #pragma .+"]),
     medium('Pragma warning messages',
            [r".*: warning: .+W#pragma-messages"]),
+    medium('Pragma once in main file',
+           [r".*: warning: #pragma once in main file .+Wpragma-once-outside-header.*"]),
     medium('Variable might be clobbered by longjmp or vfork',
            [r".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'"]),
     medium('Argument might be clobbered by longjmp or vfork',
@@ -289,6 +297,7 @@
            [r".*: warning: declaration 'class .+' does not declare anything"]),
     medium('Initialization order will be different',
            [r".*: warning: '.+' will be initialized after",
+            r".*: warning: initializer order does not match the declaration order",
             r".*: warning: field .+ will be initialized after .+Wreorder"]),
     skip('skip,   ....',
          [r".*: warning:   '.+'"]),
@@ -333,7 +342,7 @@
     low('Deprecated register',
         [r".*: warning: 'register' storage class specifier is deprecated"]),
     low('Converts between pointers to integer types with different sign',
-        [r".*: warning: .+ converts between pointers to integer types with different sign"]),
+        [r".*: warning: .+ converts between pointers to integer types .+Wpointer-sign\]"]),
     harmless('Extra tokens after #endif',
              [r".*: warning: extra tokens at end of #endif directive"]),
     medium('Comparison between different enums',
@@ -410,10 +419,37 @@
         [r".*: warning: missing .+Winvalid-pp-token"]),
     low('need glibc to link',
         [r".*: warning: .* requires at runtime .* glibc .* for linking"]),
+    low('Add braces to avoid dangling else',
+        [r".*: warning: add explicit braces to avoid dangling else"]),
+    low('Assigning value to self',
+        [r".*: warning: explicitly assigning value of .+ to itself"]),
+    low('Comparison of integers of different signs',
+        [r".*: warning: comparison of integers of different signs.+sign-compare"]),
+    low('Incompatible pointer types',
+        [r".*: warning: incompatible .*pointer types .*-Wincompatible-.*pointer-types"]),
+    low('Missing braces',
+        [r".*: warning: suggest braces around initialization of",
+         r".*: warning: too many braces around scalar initializer .+Wmany-braces-around-scalar-init",
+         r".*: warning: braces around scalar initializer"]),
+    low('Missing field initializers',
+        [r".*: warning: missing field '.+' initializer"]),
+    low('Typedef redefinition',
+        [r".*: warning: redefinition of typedef '.+' is a C11 feature"]),
+    low('GNU old-style field designator',
+        [r".*: warning: use of GNU old-style field designator extension"]),
+    low('Initializer overrides prior initialization',
+        [r".*: warning: initializer overrides prior initialization of this subobject"]),
+    low('GNU extension, variable sized type not at end',
+        [r".*: warning: field '.+' with variable sized type '.+' not at the end of a struct or class"]),
+    low('Comparison of constant is always false/true',
+        [r".*: comparison of .+ is always .+Wtautological-constant-out-of-range-compare"]),
+    low('Hides overloaded virtual function',
+        [r".*: '.+' hides overloaded virtual function"]),
     medium('Operator new returns NULL',
            [r".*: warning: 'operator new' must not return NULL unless it is declared 'throw\(\)' .+"]),
     medium('NULL used in arithmetic',
            [r".*: warning: NULL used in arithmetic",
+            r".*: warning: .* subtraction with a null pointer",
             r".*: warning: comparison between NULL and non-pointer"]),
     medium('Misspelled header guard',
            [r".*: warning: '.+' is used as a header guard .+ followed by .+ different macro"]),
diff --git a/tools/warn/html_writer.py b/tools/warn/html_writer.py
index ac5d4b7..ef173bc 100644
--- a/tools/warn/html_writer.py
+++ b/tools/warn/html_writer.py
@@ -328,7 +328,8 @@
     cur_row_class = 1 - cur_row_class
     # remove last '\n'
     out_text = text[:-1] if text[-1] == '\n' else text
-    writer('<tr><td class="c' + str(cur_row_class) + '">' + out_text + '</td></tr>')
+    writer('<tr><td class="c' + str(cur_row_class) + '">'
+           + out_text + '</td></tr>')
   writer('</table></div>')
   writer('</blockquote>')
 
@@ -355,7 +356,8 @@
   sort_warnings(warn_patterns)
   total = 0
   for severity in Severity.levels:
-    total += write_severity(csvwriter, severity, severity.column_header, warn_patterns)
+    total += write_severity(
+        csvwriter, severity, severity.column_header, warn_patterns)
   csvwriter.writerow([total, '', 'All warnings'])
 
 
diff --git a/tools/warn/java_warn_patterns.py b/tools/warn/java_warn_patterns.py
index 534f48d..3f5da9d 100644
--- a/tools/warn/java_warn_patterns.py
+++ b/tools/warn/java_warn_patterns.py
@@ -74,6 +74,8 @@
                 [r'.*\.class\): warning: Cannot find annotation method .+ in']),
     java_medium('No class/method in SDK ...',
                 [r'.*\.java:.*: warning: No such (class|method) .* for SDK']),
+    java_medium('Unknown enum constant',
+                [r'unknown_source_file: warning: unknown enum constant .+']),
     # Warnings generated by Error Prone
     java_medium('Non-ascii characters used, but ascii encoding specified',
                 [r".*: warning: unmappable character for encoding ascii"]),
@@ -207,6 +209,8 @@
            'Logging or rethrowing exceptions should usually be preferred to catching and calling printStackTrace'),
     medium('CatchFail',
            'Ignoring exceptions and calling fail() is unnecessary, and makes test output less useful'),
+    medium('ChangedAbstract',
+           'Method has changed \'abstract\' qualifier'),
     medium('ClassCanBeStatic',
            'Inner class is non-static but does not reference enclosing class'),
     medium('ClassNewInstance',
@@ -355,6 +359,8 @@
            'equals method doesn\'t override Object.equals'),
     medium('NotCloseable',
            'Not closeable'),
+    medium('NullableCollection',
+           'Method should not return a nullable collection'),
     medium('NullableConstructor',
            'Constructors should not be annotated with @Nullable since they cannot return null'),
     medium('NullableDereference',
@@ -801,6 +807,8 @@
                 [r".*: warning: \[path\] bad path element .*\.jar"]),
     java_medium('Supported version from annotation processor',
                 [r".*: warning: Supported source version .+ from annotation processor"]),
+    java_medium('Schema export directory is not provided',
+                [r".*\.(java|kt):.*: warning: Schema export directory is not provided"]),
 ]
 
 compile_patterns(warn_patterns)
diff --git a/tools/warn/make_warn_patterns.py b/tools/warn/make_warn_patterns.py
index a54c502..11ad5cc 100644
--- a/tools/warn/make_warn_patterns.py
+++ b/tools/warn/make_warn_patterns.py
@@ -35,6 +35,9 @@
     {'category': 'make', 'severity': Severity.HIGH,
      'description': 'System module linking to a vendor module',
      'patterns': [r".*: warning: .+ \(.+\) should not link to .+ \(partition:.+\)"]},
+    {'category': 'make', 'severity': Severity.HIGH,
+     'description': 'make: lstat file does not exist',
+     'patterns': [r".*: warning: lstat .+: file does not exist"]},
     {'category': 'make', 'severity': Severity.MEDIUM,
      'description': 'Invalid SDK/NDK linking',
      'patterns': [r".*: warning: .+ \(.+\) should not link to .+ \(.+\)"]},
@@ -56,6 +59,9 @@
     {'category': 'make', 'severity': Severity.MEDIUM,
      'description': 'make: deprecated macros',
      'patterns': [r".*\.mk:.* warning:.* [A-Z_]+ (is|has been) deprecated."]},
+    {'category': 'make', 'severity': Severity.MEDIUM,
+     'description': 'make: other Android.mk warnings',
+     'patterns': [r".*/Android.mk:.*: warning: .+"]},
 ]
 
 
diff --git a/tools/warn/other_warn_patterns.py b/tools/warn/other_warn_patterns.py
index d05c8e9..c95528c 100644
--- a/tools/warn/other_warn_patterns.py
+++ b/tools/warn/other_warn_patterns.py
@@ -75,37 +75,15 @@
     # misc warnings
     misc('Duplicate logtag',
          [r".*: warning: tag \".+\" \(.+\) duplicated in .+"]),
-    misc('Typedef redefinition',
-         [r".*: warning: redefinition of typedef '.+' is a C11 feature"]),
-    misc('GNU old-style field designator',
-         [r".*: warning: use of GNU old-style field designator extension"]),
-    misc('Missing field initializers',
-         [r".*: warning: missing field '.+' initializer"]),
-    misc('Missing braces',
-         [r".*: warning: suggest braces around initialization of",
-          r".*: warning: too many braces around scalar initializer .+Wmany-braces-around-scalar-init",
-          r".*: warning: braces around scalar initializer"]),
-    misc('Comparison of integers of different signs',
-         [r".*: warning: comparison of integers of different signs.+sign-compare"]),
-    misc('Add braces to avoid dangling else',
-         [r".*: warning: add explicit braces to avoid dangling else"]),
-    misc('Initializer overrides prior initialization',
-         [r".*: warning: initializer overrides prior initialization of this subobject"]),
-    misc('Assigning value to self',
-         [r".*: warning: explicitly assigning value of .+ to itself"]),
-    misc('GNU extension, variable sized type not at end',
-         [r".*: warning: field '.+' with variable sized type '.+' not at the end of a struct or class"]),
-    misc('Comparison of constant is always false/true',
-         [r".*: comparison of .+ is always .+Wtautological-constant-out-of-range-compare"]),
-    misc('Hides overloaded virtual function',
-         [r".*: '.+' hides overloaded virtual function"]),
-    misc('Incompatible pointer types',
-         [r".*: warning: incompatible .*pointer types .*-Wincompatible-.*pointer-types"]),
     # Assembler warnings
     asm('ASM value size does not match register size',
         [r".*: warning: value size does not match register size specified by the constraint and modifier"]),
     asm('IT instruction is deprecated',
         [r".*: warning: applying IT instruction .* is deprecated"]),
+    asm('section flags ignored',
+        [r".*: warning: section flags ignored on section redeclaration"]),
+    asm('setjmp/longjmp/vfork changed binding',
+        [r".*: warning: .*(setjmp|longjmp|vfork) changed binding to .*"]),
     # NDK warnings
     {'category': 'NDK', 'severity': Severity.HIGH,
      'description': 'NDK: Generate guard with empty availability, obsoleted',
@@ -168,6 +146,9 @@
     {'category': 'RenderScript', 'severity': Severity.LOW,
      'description': 'RenderScript warnings',
      'patterns': [r'.*\.rscript:.*: warning: ']},
+    {'category': 'RenderScript', 'severity': Severity.HIGH,
+     'description': 'RenderScript is deprecated',
+     'patterns': [r'.*: warning: Renderscript is deprecated:.+']},
     # Broken/partial warning messages will be skipped.
     {'category': 'Misc', 'severity': Severity.SKIP,
      'description': 'skip, ,',
diff --git a/tools/warn/tidy_warn_patterns.py b/tools/warn/tidy_warn_patterns.py
index 7018d10..a5842ea 100644
--- a/tools/warn/tidy_warn_patterns.py
+++ b/tools/warn/tidy_warn_patterns.py
@@ -23,15 +23,19 @@
 from .severity import Severity
 
 
-def tidy_warn_pattern(description, pattern):
+def tidy_warn(description, patterns):
   return {
       'category': 'C/C++',
       'severity': Severity.TIDY,
       'description': 'clang-tidy ' + description,
-      'patterns': [r'.*: .+\[' + pattern + r'\]$']
+      'patterns': patterns,
   }
 
 
+def tidy_warn_pattern(description, pattern):
+  return tidy_warn(description, [r'.*: .+\[' + pattern + r'\]$'])
+
+
 def simple_tidy_warn_pattern(description):
   return tidy_warn_pattern(description, description)
 
@@ -77,6 +81,7 @@
 warn_patterns = [
     # pylint does not recognize g-inconsistent-quotes
     # pylint:disable=line-too-long,bad-option-value,g-inconsistent-quotes
+    group_tidy_warn_pattern('altera'),
     group_tidy_warn_pattern('android'),
     simple_tidy_warn_pattern('abseil-string-find-startswith'),
     simple_tidy_warn_pattern('bugprone-argument-comment'),
@@ -123,8 +128,9 @@
     simple_tidy_warn_pattern('cert-oop54-cpp'),
     group_tidy_warn_pattern('cert'),
     group_tidy_warn_pattern('clang-diagnostic'),
+    group_tidy_warn_pattern('concurrency'),
     group_tidy_warn_pattern('cppcoreguidelines'),
-    group_tidy_warn_pattern('llvm'),
+    group_tidy_warn_pattern('fuchsia'),
     simple_tidy_warn_pattern('google-default-arguments'),
     simple_tidy_warn_pattern('google-runtime-int'),
     simple_tidy_warn_pattern('google-runtime-operator'),
@@ -148,8 +154,10 @@
     simple_tidy_warn_pattern('hicpp-noexcept-move'),
     simple_tidy_warn_pattern('hicpp-use-override'),
     group_tidy_warn_pattern('hicpp'),
-    group_tidy_warn_pattern('modernize'),
+    group_tidy_warn_pattern('llvm'),
+    group_tidy_warn_pattern('llvmlibc'),
     group_tidy_warn_pattern('misc'),
+    group_tidy_warn_pattern('modernize'),
     simple_tidy_warn_pattern('performance-faster-string-find'),
     simple_tidy_warn_pattern('performance-for-range-copy'),
     simple_tidy_warn_pattern('performance-implicit-cast-in-loop'),
@@ -168,6 +176,9 @@
     simple_tidy_warn_pattern('portability-simd-intrinsics'),
     group_tidy_warn_pattern('portability'),
 
+    tidy_warn('TIMEOUT', [r".*: warning: clang-tidy aborted "]),
+    tidy_warn('Long Runs', [r".*: warning: clang-tidy used "]),
+
     # warnings from clang-tidy's clang-analyzer checks
     analyzer_high('clang-analyzer-core, null pointer',
                   [r".*: warning: .+ pointer is null .*\[clang-analyzer-core"]),
diff --git a/tools/warn/warn_common.py b/tools/warn/warn_common.py
index 844f629..61c8676 100755
--- a/tools/warn/warn_common.py
+++ b/tools/warn/warn_common.py
@@ -236,7 +236,11 @@
   warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
   count = 0
   for line in buildlog:
-    if warning_pattern.match(line):
+    # We want to find android_root of a local build machine.
+    # Do not use RBE warning lines, which has '/b/f/w/' path prefix.
+    # Do not use /tmp/ file warnings.
+    if warning_pattern.match(line) and (
+        '/b/f/w' not in line and not line.startswith('/tmp/')):
       warning_lines.append(line)
       count += 1
       if count > 9999:
@@ -300,6 +304,7 @@
   architecture = 'unknown'
 
   # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
+  # Bug: http://198657613, This might need change to handle RBE output.
   chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
 
   warning_pattern = re.compile(chrome_warning_pattern)
@@ -347,6 +352,8 @@
   platform_version = 'unknown'
   target_product = 'unknown'
   target_variant = 'unknown'
+  build_id = 'unknown'
+  use_rbe = False
   android_root = find_android_root(infile)
   infile.seek(0)
 
@@ -363,6 +370,14 @@
   warning_without_file = re.compile('^warning: .*')
   rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
 
+  # If RBE was used, try to reclaim some warning lines mixed with some
+  # leading chars from other concurrent job's stderr output .
+  # The leading characters can be any character, including digits and spaces.
+  # It's impossible to correctly identify the starting point of the source
+  # file path without the file directory name knowledge.
+  # Here we can only be sure to recover lines containing "/b/f/w/".
+  rbe_warning_pattern = re.compile('.*/b/f/w/[^ ]*: warning: .*')
+
    # Collect all unique warning lines
   # Remove the duplicated warnings save ~8% of time when parsing
   # one typical build log than before
@@ -384,6 +399,12 @@
           prev_warning, flags, android_root, unique_warnings)
       prev_warning = ''
 
+    if use_rbe and rbe_warning_pattern.match(line):
+      cleaned_up_line = re.sub('.*/b/f/w/', '', line)
+      unique_warnings = add_normalized_line_to_warnings(
+          cleaned_up_line, flags, android_root, unique_warnings)
+      continue
+
     if warning_pattern.match(line):
       if warning_without_file.match(line):
         # save this line and combine it with the next line
@@ -399,15 +420,26 @@
       result = re.search('(?<=^PLATFORM_VERSION=).*', line)
       if result is not None:
         platform_version = result.group(0)
+        continue
       result = re.search('(?<=^TARGET_PRODUCT=).*', line)
       if result is not None:
         target_product = result.group(0)
+        continue
       result = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
       if result is not None:
         target_variant = result.group(0)
+        continue
+      result = re.search('(?<=^BUILD_ID=).*', line)
+      if result is not None:
+        build_id = result.group(0)
+        continue
       result = re.search('(?<=^TOP=).*', line)
       if result is not None:
         android_root = result.group(1)
+        continue
+      if re.search('USE_RBE=', line) is not None:
+        use_rbe = True
+        continue
 
   if android_root:
     new_unique_warnings = dict()
@@ -418,8 +450,8 @@
           warning_line, flags, android_root)
     unique_warnings = new_unique_warnings
 
-  header_str = '%s - %s - %s' % (platform_version, target_product,
-                                 target_variant)
+  header_str = '%s - %s - %s (%s)' % (
+      platform_version, target_product, target_variant, build_id)
   return unique_warnings, header_str
 
 
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
index 47ebd12..53fc8d4 100644
--- a/tools/zipalign/ZipAlignMain.cpp
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -20,6 +20,7 @@
 
 #include "ZipAlign.h"
 
+#include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -60,69 +61,53 @@
     int alignment;
     char* endp;
 
-    if (argc < 4) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    argc--;
-    argv++;
-
-    while (argc && argv[0][0] == '-') {
-        const char* cp = argv[0] +1;
-
-        while (*cp != '\0') {
-            switch (*cp) {
-            case 'c':
-                check = true;
-                break;
-            case 'f':
-                force = true;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            case 'z':
-                zopfli = true;
-                break;
-            case 'p':
-                pageAlignSharedLibs = true;
-                break;
-            default:
-                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
-                wantUsage = true;
-                goto bail;
-            }
-
-            cp++;
+    int opt;
+    while ((opt = getopt(argc, argv, "fcpvz")) != -1) {
+        switch (opt) {
+        case 'c':
+            check = true;
+            break;
+        case 'f':
+            force = true;
+            break;
+        case 'v':
+            verbose = true;
+            break;
+        case 'z':
+            zopfli = true;
+            break;
+        case 'p':
+            pageAlignSharedLibs = true;
+            break;
+        default:
+            fprintf(stderr, "ERROR: unknown flag -%c\n", opt);
+            wantUsage = true;
+            goto bail;
         }
-
-        argc--;
-        argv++;
     }
 
-    if (!((check && argc == 2) || (!check && argc == 3))) {
+    if (!((check && (argc - optind) == 2) || (!check && (argc - optind) == 3))) {
         wantUsage = true;
         goto bail;
     }
 
-    alignment = strtol(argv[0], &endp, 10);
+    alignment = strtol(argv[optind], &endp, 10);
     if (*endp != '\0' || alignment <= 0) {
-        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+        fprintf(stderr, "Invalid value for alignment: %s\n", argv[optind]);
         wantUsage = true;
         goto bail;
     }
 
     if (check) {
         /* check existing archive for correct alignment */
-        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
+        result = verify(argv[optind + 1], alignment, verbose, pageAlignSharedLibs);
     } else {
         /* create the new archive */
-        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
+        result = process(argv[optind + 1], argv[optind + 2], alignment, force, zopfli, pageAlignSharedLibs);
 
         /* trust, but verify */
         if (result == 0) {
-            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+            result = verify(argv[optind + 2], alignment, verbose, pageAlignSharedLibs);
         }
     }
 
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
index 5233f0a..fcad96c 100644
--- a/tools/zipalign/ZipEntry.cpp
+++ b/tools/zipalign/ZipEntry.cpp
@@ -87,7 +87,7 @@
     }
 
     /*
-     * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
+     * Check the LFH.  Note that this will fail if the "kUsesDataDescr"
      * flag is set, because the LFH is incomplete.  (Not a problem, since we
      * prefer the CDE values.)
      */
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
index 1e3c413..f2f65a6 100644
--- a/tools/zipalign/ZipFile.cpp
+++ b/tools/zipalign/ZipFile.cpp
@@ -530,7 +530,7 @@
     // If the alignment is not what was requested, add some padding in the extra
     // so the payload ends up where is requested.
     uint64_t alignDiff = alignTo - (expectedPayloadOffset % alignTo);
-    if (alignDiff == 0)
+    if (alignDiff == alignTo)
         return OK;
 
     return pEntry->addPadding(alignDiff);
@@ -654,7 +654,7 @@
 {
     ZipEntry* pEntry = NULL;
     status_t result;
-    long lfhPosn, startPosn, endPosn, uncompressedLen;
+    long lfhPosn, uncompressedLen;
 
     if (mReadOnly)
         return INVALID_OPERATION;
@@ -690,7 +690,6 @@
      */
     lfhPosn = ftell(mZipFp);
     pEntry->mLFH.write(mZipFp);
-    startPosn = ftell(mZipFp);
 
     /*
      * Copy the data over.
@@ -741,18 +740,13 @@
     }
 
     /*
-     * Update file offsets.
-     */
-    endPosn = ftell(mZipFp);
-
-    /*
      * Success!  Fill out new values.
      */
     pEntry->setLFHOffset(lfhPosn);
     mEOCD.mNumEntries++;
     mEOCD.mTotalNumEntries++;
     mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
-    mEOCD.mCentralDirOffset = endPosn;
+    mEOCD.mCentralDirOffset = ftell(mZipFp);
 
     /*
      * Go back and write the LFH.
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
index c79e791..96d4f73 100644
--- a/tools/zipalign/tests/src/align_test.cpp
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -9,6 +9,7 @@
 #include <android-base/file.h>
 
 using namespace android;
+using namespace base;
 
 static std::string GetTestPath(const std::string& filename) {
   static std::string test_data_dir = android::base::GetExecutableDirectory() + "/tests/data/";
@@ -26,6 +27,34 @@
   ASSERT_EQ(0, verified);
 }
 
+TEST(Align, DoubleAligment) {
+  const std::string src = GetTestPath("unaligned.zip");
+  const std::string tmp = GetTestPath("da_aligned.zip");
+  const std::string dst = GetTestPath("da_d_aligner.zip");
+
+  int processed = process(src.c_str(), tmp.c_str(), 4, true, false, 4096);
+  ASSERT_EQ(0, processed);
+
+  int verified = verify(tmp.c_str(), 4, true, false);
+  ASSERT_EQ(0, verified);
+
+  // Align the result of the previous run. Essentially double aligning.
+  processed = process(tmp.c_str(), dst.c_str(), 4, true, false, 4096);
+  ASSERT_EQ(0, processed);
+
+  verified = verify(dst.c_str(), 4, true, false);
+  ASSERT_EQ(0, verified);
+
+  // Nothing should have changed between tmp and dst.
+  std::string tmp_content;
+  ASSERT_EQ(true, ReadFileToString(tmp, &tmp_content));
+
+  std::string dst_content;
+  ASSERT_EQ(true, ReadFileToString(dst, &dst_content));
+
+  ASSERT_EQ(tmp_content, dst_content);
+}
+
 // Align a zip featuring a hole at the beginning. The
 // hole in the archive is a delete entry in the Central
 // Directory.