Merge "Check for duplicate entries in build.prop in validation script"
diff --git a/Changes.md b/Changes.md
index 453ea6c..61e6bb6 100644
--- a/Changes.md
+++ b/Changes.md
@@ -1,5 +1,92 @@
 # Build System Changes for Android.mk Writers
 
+## Changes in system properties settings
+
+### Product variables
+
+System properties for each of the partition is supposed to be set via following
+product config variables.
+
+For system partititon,
+
+* `PRODUCT_SYSTEM_PROPERITES`
+* `PRODUCT_SYSTEM_DEFAULT_PROPERTIES` is highly discouraged. Will be deprecated.
+
+For vendor partition,
+
+* `PRODUCT_VENDOR_PROPERTIES`
+* `PRODUCT_PROPERTY_OVERRIDES` is highly discouraged. Will be deprecated.
+* `PRODUCT_DEFAULT_PROPERTY_OVERRIDES` is also discouraged. Will be deprecated.
+
+For odm partition,
+
+* `PRODUCT_ODM_PROPERTIES`
+
+For system_ext partition,
+
+* `PRODUCT_SYSTEM_EXT_PROPERTIES`
+
+For product partition,
+
+* `PRODUCT_PRODUCT_PROPERTIES`
+
+### Duplication is not allowed within a partition
+
+For each partition, having multiple sysprop assignments for the same name is
+prohibited. For example, the following will now trigger an error:
+
+`PRODUCT_VENDOR_PROPERTIES += foo=true foo=false`
+
+Having duplication across partitions are still allowed. So, the following is
+not an error:
+
+`PRODUCT_VENDOR_PROPERTIES += foo=true`
+`PRODUCT_SYSTEM_PROPERTIES += foo=false`
+
+In that case, the final value is determined at runtime. The precedence is
+
+* product
+* odm
+* vendor
+* system_ext
+* system
+
+So, `foo` becomes `true` because vendor has higher priority than system.
+
+To temporarily turn the build-time restriction off, use
+
+`BUILD_BROKEN_DUP_SYSPROP := true`
+
+### Optional assignments
+
+System properties can now be set as optional using the new syntax:
+
+`name ?= value`
+
+Then the system property named `name` gets the value `value` only when there
+is no other non-optional assignments having the same name. For example, the
+following is allowed and `foo` gets `true`
+
+`PRODUCT_VENDOR_PROPERTIES += foo=true foo?=false`
+
+Note that the order between the optional and the non-optional assignments
+doesn't matter. The following gives the same result as above.
+
+`PRODUCT_VENDOR_PROPERTIES += foo?=false foo=true`
+
+Optional assignments can be duplicated and in that case their order matters.
+Specifically, the last one eclipses others.
+
+`PRODUCT_VENDOR_PROPERTIES += foo?=apple foo?=banana foo?=mango`
+
+With above, `foo` becomes `mango` since its the last one.
+
+Note that this behavior is different from the previous behavior of preferring
+the first one. To go back to the original behavior for compatability reason,
+use:
+
+`BUILD_BROKEN_DUP_SYSPROP := true`
+
 ## ELF prebuilts in PRODUCT_COPY_FILES
 
 ELF prebuilts in PRODUCT_COPY_FILES that are installed into these paths are an
diff --git a/core/Makefile b/core/Makefile
index 27f8756..2f5b621 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -130,7 +130,7 @@
 
 # -----------------------------------------------------------------
 # docs/index.html
-ifeq (,$(TARGET_BUILD_APPS))
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 gen := $(OUT_DOCS)/index.html
 ALL_DOCS += $(gen)
 $(gen): frameworks/base/docs/docs-redirect-index.html
@@ -206,7 +206,7 @@
 
 define copy-and-strip-kernel-module
 $(2): $(1)
-	$($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_STRIP) -o $(2) --strip-debug $(1)
+	$(LLVM_STRIP) -o $(2) --strip-debug $(1)
 endef
 
 # $(1): modules list
@@ -340,6 +340,15 @@
     $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_ramdisk_recovery_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.recovery,$(TARGET_VENDOR_RAMDISK_OUT))))
 endef
 
+# $(1): kernel module directory name (top is an out of band value for no directory)
+define build-vendor-charger-load
+$(if $(filter top,$(1)),\
+  $(eval _kver :=)$(eval _sep :=),\
+  $(eval _kver := $(1))$(eval _sep :=_))\
+  $(if $(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),\
+    $(call copy-many-files,$(call module-load-list-copy-paths,$(call intermediates-dir-for,PACKAGING,vendor_charger_module_list$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES$(_sep)$(_kver)),$(BOARD_VENDOR_CHARGER_KERNEL_MODULES_LOAD$(_sep)$(_kver)),modules.load.charger,$(TARGET_OUT_VENDOR))))
+endef
+
 ifneq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
   # If there is no vendor boot partition, store vendor ramdisk kernel modules in the
   # boot ramdisk.
@@ -375,6 +384,7 @@
   $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR_RAMDISK,$(TARGET_VENDOR_RAMDISK_OUT),,modules.load,$(VENDOR_RAMDISK_STRIPPED_MODULE_STAGING_DIR),$(dir))) \
   $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-ramdisk-recovery-load,$(dir))) \
   $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,VENDOR,$(TARGET_OUT_VENDOR),vendor,modules.load,$(VENDOR_STRIPPED_MODULE_STAGING_DIR),$(dir))) \
+  $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-vendor-charger-load,$(dir))) \
   $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,ODM,$(TARGET_OUT_ODM),odm,modules.load,,$(dir))) \
   $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\
     $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-recovery-as-boot-load,$(dir))),\
@@ -397,6 +407,16 @@
 
 endef
 
+# -----------------------------------------------------------------
+# Merge an individual apkcerts output into the final apkcerts.txt output.
+# Use a macro to make it compatible with _apkcerts_write_line
+# $1 apkcerts file to be merged
+# $2 output file
+define _apkcerts_merge
+$(hide) cat $1 >> $2
+
+endef
+
 name := $(TARGET_PRODUCT)
 ifeq ($(TARGET_BUILD_TYPE),debug)
   name := $(name)_debug
@@ -405,6 +425,8 @@
 intermediates := \
 	$(call intermediates-dir-for,PACKAGING,apkcerts)
 APKCERTS_FILE := $(intermediates)/$(name).txt
+all_apkcerts_files := $(sort $(foreach p,$(PACKAGES),$(PACKAGES.$(p).APKCERTS_FILE)))
+$(APKCERTS_FILE): $(all_apkcerts_files)
 # We don't need to really build all the modules.
 # TODO: rebuild APKCERTS_FILE if any app change its cert.
 $(APKCERTS_FILE):
@@ -412,9 +434,11 @@
 	@mkdir -p $(dir $@)
 	@rm -f $@
 	$(foreach p,$(sort $(PACKAGES)),\
-	  $(if $(PACKAGES.$(p).EXTERNAL_KEY),\
-	    $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),"EXTERNAL","",$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\
-	    $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@)))
+	  $(if $(PACKAGES.$(p).APKCERTS_FILE),\
+	    $(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\
+	    $(if $(PACKAGES.$(p).EXTERNAL_KEY),\
+	      $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),"EXTERNAL","",$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@),\
+	      $(call _apkcerts_write_line,$(PACKAGES.$(p).STEM),$(PACKAGES.$(p).CERTIFICATE),$(PACKAGES.$(p).PRIVATE_KEY),$(PACKAGES.$(p).COMPRESSED),$(PACKAGES.$(p).PARTITION),$@))))
 	# In case value of PACKAGES is empty.
 	$(hide) touch $@
 
@@ -813,7 +837,7 @@
     $(ALL_GENERATED_SOURCES) \
     $(ALL_DEFAULT_INSTALLED_MODULES))
 
-INTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor-boot)/vendor-ramdisk.cpio.gz
+INTERNAL_VENDOR_RAMDISK_TARGET := $(call intermediates-dir-for,PACKAGING,vendor-boot)/vendor-ramdisk.cpio$(RAMDISK_EXT)
 $(INTERNAL_VENDOR_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_VENDOR_RAMDISK_FILES) | $(COMPRESSION_COMMAND_DEPS)
 	$(MKBOOTFS) -d $(TARGET_OUT) $(TARGET_VENDOR_RAMDISK_OUT) | $(COMPRESSION_COMMAND) > $@
 
@@ -1201,6 +1225,10 @@
     $(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)
+)
+$(if $(filter $(2),system_other),\
+    $(hide) echo "building_system_other_image=$(BUILDING_SYSTEM_OTHER_IMAGE)" >> $(1)
 )
 $(if $(filter $(2),userdata),\
     $(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1208,11 +1236,13 @@
     $(if $(PRODUCT_FS_CASEFOLD),$(hide) echo "needs_casefold=$(PRODUCT_FS_CASEFOLD)" >> $(1))
     $(if $(PRODUCT_QUOTA_PROJID),$(hide) echo "needs_projid=$(PRODUCT_QUOTA_PROJID)" >> $(1))
     $(hide) echo "userdata_selinux_fc=$(SELINUX_FC)" >> $(1)
+    $(hide) echo "building_userdata_image=$(BUILDING_USERDATA_IMAGE)" >> $(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)
 )
 $(if $(filter $(2),vendor),\
     $(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1227,6 +1257,7 @@
     $(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)
 )
 $(if $(filter $(2),product),\
     $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "product_fs_type=$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1241,6 +1272,7 @@
     $(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)
 )
 $(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))
@@ -1254,6 +1286,7 @@
     $(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)
 )
 $(if $(filter $(2),odm),\
     $(if $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "odm_fs_type=$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1268,6 +1301,7 @@
     $(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)
 )
 $(if $(filter $(2),oem),\
     $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
@@ -1362,6 +1396,9 @@
 ifdef BUILDING_SYSTEM_IMAGE
   PROP_DICTIONARY_IMAGES += system
 endif
+ifdef BUILDING_SYSTEM_OTHER_IMAGE
+  PROP_DICTIONARY_IMAGES += system_other
+endif
 ifdef BUILDING_USERDATA_IMAGE
   PROP_DICTIONARY_IMAGES += userdata
 endif
@@ -1761,52 +1798,37 @@
       $(AVBTOOL) add_hash_footer --image $(1) --partition_size $(BOARD_RECOVERYIMAGE_PARTITION_SIZE) --partition_name recovery $(INTERNAL_AVB_RECOVERY_SIGNING_ARGS) $(BOARD_AVB_RECOVERY_ADD_HASH_FOOTER_ARGS)))
 endef
 
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+recoveryimage-deps := $(MKBOOTIMG) $(recovery_ramdisk) $(recovery_kernel)
 ifeq (true,$(PRODUCT_SUPPORTS_BOOT_SIGNER))
-$(INSTALLED_BOOTIMAGE_TARGET) : $(BOOT_SIGNER)
+  recoveryimage-deps += $(BOOT_SIGNER)
 endif
 ifeq (true,$(PRODUCT_SUPPORTS_VBOOT))
-$(INSTALLED_BOOTIMAGE_TARGET) : $(VBOOT_SIGNER)
+  recoveryimage-deps += $(VBOOT_SIGNER)
 endif
 ifeq (true,$(BOARD_AVB_ENABLE))
-$(INSTALLED_BOOTIMAGE_TARGET) : $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)
+  recoveryimage-deps += $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)
 endif
 ifdef BOARD_INCLUDE_RECOVERY_DTBO
-ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE
-$(INSTALLED_BOOTIMAGE_TARGET): $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)
-else
-$(INSTALLED_BOOTIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE)
-endif
+  ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE
+    recoveryimage-deps += $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)
+  else
+    recoveryimage-deps += $(BOARD_PREBUILT_DTBOIMAGE)
+  endif
 endif
 ifdef BOARD_INCLUDE_RECOVERY_ACPIO
-$(INSTALLED_BOOTIMAGE_TARGET): $(BOARD_RECOVERY_ACPIO)
+  recoveryimage-deps += $(BOARD_RECOVERY_ACPIO)
 endif
 ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
-$(INSTALLED_BOOTIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET)
+  recoveryimage-deps += $(INSTALLED_DTBIMAGE_TARGET)
 endif
 
-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(recovery_ramdisk) \
-	    $(recovery_kernel)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+$(INSTALLED_BOOTIMAGE_TARGET): $(recoveryimage-deps)
 	$(call pretty,"Target boot image from recovery: $@")
 	$(call build-recoveryimage-target, $@, $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $@))))
 endif # BOARD_USES_RECOVERY_AS_BOOT
 
-ifdef BOARD_INCLUDE_RECOVERY_DTBO
-ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)
-else
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE)
-endif
-endif
-ifdef BOARD_INCLUDE_RECOVERY_ACPIO
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(BOARD_RECOVERY_ACPIO)
-endif
-ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(INSTALLED_DTBIMAGE_TARGET)
-endif
-
-$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTIMG) $(recovery_ramdisk) \
-	    $(recovery_kernel)
+$(INSTALLED_RECOVERYIMAGE_TARGET): $(recoveryimage-deps)
 	$(call build-recoveryimage-target, $@, $(recovery_kernel))
 
 ifdef RECOVERY_RESOURCE_ZIP
@@ -3072,6 +3094,9 @@
 # configured as a chained partition, if BOARD_AVB_<partition>_KEY_PATH is defined. Otherwise the
 # image descriptor will be included into vbmeta.img, unless it has been already added to any chained
 # VBMeta image.
+# Multiple boot images can be generated based on BOARD_KERNEL_BINARIES
+# but vbmeta would capture the image descriptor of only the first boot
+# image specified in BUILT_BOOTIMAGE_TARGET.
 # $(1): Partition name, e.g. boot or system.
 define check-and-set-avb-args
 $(eval _in_chained_vbmeta := $(filter $(1),$(INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES)))
@@ -3080,8 +3105,11 @@
         $(error Chaining partition "$(1)" in chained VBMeta image is not supported)) \
     $(call _check-and-set-avb-chain-args,$(1)),\
     $(if $(_in_chained_vbmeta),,\
-        $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
-            --include_descriptors_from_image $(call images-for-partitions,$(1)))))
+        $(if $(filter boot,$(1)),\
+            $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
+                --include_descriptors_from_image $(firstword $(call images-for-partitions,$(1)))),\
+            $(eval INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
+                --include_descriptors_from_image $(call images-for-partitions,$(1))))))
 endef
 
 # Checks and sets build variables for a custom chained partition to include it into vbmeta.img.
@@ -3306,7 +3334,7 @@
 # -----------------------------------------------------------------
 # Check VINTF of build
 
-ifndef TARGET_BUILD_APPS
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 intermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all)
 check_vintf_all_deps :=
 
@@ -3431,8 +3459,8 @@
 $(BUILT_KERNEL_CONFIGS_FILE): .KATI_IMPLICIT_OUTPUTS := $(BUILT_KERNEL_VERSION_FILE)
 $(BUILT_KERNEL_CONFIGS_FILE): PRIVATE_DECOMPRESS_TOOLS := $(my_decompress_tools)
 $(BUILT_KERNEL_CONFIGS_FILE): $(foreach pair,$(my_decompress_tools),$(call word-colon,2,$(pair)))
-$(BUILT_KERNEL_CONFIGS_FILE): $(EXTRACT_KERNEL) $(INSTALLED_KERNEL_TARGET)
-	$< --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(INSTALLED_KERNEL_TARGET) \
+$(BUILT_KERNEL_CONFIGS_FILE): $(EXTRACT_KERNEL) $(firstword $(INSTALLED_KERNEL_TARGET))
+	$< --tools $(PRIVATE_DECOMPRESS_TOOLS) --input $(firstword $(INSTALLED_KERNEL_TARGET)) \
 	  --output-configs $@ \
 	  --output-version $(BUILT_KERNEL_VERSION_FILE)
 
@@ -3512,13 +3540,12 @@
 check_vintf_common_srcs :=
 check_vintf_all_deps :=
 intermediates :=
-endif # !TARGET_BUILD_APPS
+endif # !TARGET_BUILD_UNBUNDLED
 
 # -----------------------------------------------------------------
 # Check image sizes <= size of super partition
 
-ifeq (,$(TARGET_BUILD_APPS))
-# Do not check for apps-only build
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 
 ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION))
 
@@ -3554,7 +3581,7 @@
 
 endif # PRODUCT_BUILD_SUPER_PARTITION
 
-endif # TARGET_BUILD_APPS
+endif # !TARGET_BUILD_UNBUNDLED
 
 # -----------------------------------------------------------------
 # bring in the installer image generation defines if necessary
@@ -3638,6 +3665,7 @@
   libconscrypt_openjdk_jni \
   lpmake \
   lpunpack \
+  lz4 \
   make_f2fs \
   merge_target_files \
   minigzip \
@@ -3774,6 +3802,9 @@
 else
 	echo "boot_images=$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(notdir $(b)))" >> $@
 endif
+ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
+	echo "lz4_ramdisks=true" >> $@
+endif
 ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)
 	echo "vendor_boot=true" >> $@
 	echo "vendor_boot_size=$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)" >> $@
@@ -3955,7 +3986,7 @@
 # We can't build static executables when SANITIZE_TARGET=address
 ifeq (,$(filter address, $(SANITIZE_TARGET)))
 built_ota_tools += \
-    $(call intermediates-dir-for,EXECUTABLES,updater,,,$(TARGET_PREFER_32_BIT))/updater
+    $(call intermediates-dir-for,EXECUTABLES,updater)/updater
 endif
 
 $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
@@ -4034,13 +4065,13 @@
   $(foreach device,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES), \
     echo "super_$(device)_device_size=$(BOARD_SUPER_PARTITION_$(call to-upper,$(device))_DEVICE_SIZE)" >> $(1);)
   $(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \
-    echo "dynamic_partition_list=$(call filter-out-missing-vendor, $(BOARD_SUPER_PARTITION_PARTITION_LIST))" >> $(1))
+    echo "dynamic_partition_list=$(call filter-out-missing-vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST))" >> $(1))
   $(if $(BOARD_SUPER_PARTITION_GROUPS),
     echo "super_partition_groups=$(BOARD_SUPER_PARTITION_GROUPS)" >> $(1))
   $(foreach group,$(BOARD_SUPER_PARTITION_GROUPS), \
     echo "super_$(group)_group_size=$(BOARD_$(call to-upper,$(group))_SIZE)" >> $(1); \
     $(if $(BOARD_$(call to-upper,$(group))_PARTITION_LIST), \
-      echo "super_$(group)_partition_list=$(call filter-out-missing-vendor, $(BOARD_$(call to-upper,$(group))_PARTITION_LIST))" >> $(1);))
+      echo "super_$(group)_partition_list=$(call filter-out-missing-vendor,$(BOARD_$(call to-upper,$(group))_PARTITION_LIST))" >> $(1);))
   $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED)), \
     echo "build_non_sparse_super_partition=true" >> $(1))
   $(if $(filter true,$(TARGET_USERIMAGES_SPARSE_F2FS_DISABLED)), \
@@ -4481,7 +4512,7 @@
 # A zip of the appcompat directory containing logs
 APPCOMPAT_ZIP := $(PRODUCT_OUT)/appcompat.zip
 # For apps_only build we'll establish the dependency later in build/make/core/main.mk.
-ifndef TARGET_BUILD_APPS
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 $(APPCOMPAT_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
 	    $(INSTALLED_RAMDISK_TARGET) \
 	    $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -4510,7 +4541,7 @@
 
 SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name).zip
 # For apps_only build we'll establish the dependency later in build/make/core/main.mk.
-ifndef TARGET_BUILD_APPS
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 $(SYMBOLS_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
 	    $(INSTALLED_RAMDISK_TARGET) \
 	    $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -4536,7 +4567,7 @@
 name := $(name)_debug
 endif
 COVERAGE_ZIP := $(PRODUCT_OUT)/$(name).zip
-ifndef TARGET_BUILD_APPS
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 $(COVERAGE_ZIP): $(INSTALLED_SYSTEMIMAGE_TARGET) \
 	    $(INSTALLED_RAMDISK_TARGET) \
 	    $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -4611,7 +4642,7 @@
 #
 PROGUARD_DICT_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-$(FILE_NAME_TAG).zip
 # For apps_only build we'll establish the dependency later in build/make/core/main.mk.
-ifndef TARGET_BUILD_APPS
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 $(PROGUARD_DICT_ZIP): \
     $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
diff --git a/core/OWNERS b/core/OWNERS
index 750f1fa..459683e 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -1,3 +1,2 @@
 per-file dex_preopt*.mk = ngeoffray@google.com,calin@google.com,mathewi@google.com,dbrazdil@google.com
-per-file construct_context.sh = ngeoffray@google.com,calin@google.com,mathieuc@google.com
 per-file verify_uses_libraries.sh = ngeoffray@google.com,calin@google.com,mathieuc@google.com
diff --git a/core/app_certificate_validate.mk b/core/app_certificate_validate.mk
index c01526a..1ccacfb 100644
--- a/core/app_certificate_validate.mk
+++ b/core/app_certificate_validate.mk
@@ -3,7 +3,7 @@
   ifneq (,$(filter $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))%,$(LOCAL_CERTIFICATE)))
     CERTIFICATE_VIOLATION_MODULES += $(LOCAL_MODULE)
     ifeq (true,$(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT))
-      $(if $(filter $(LOCAL_MODULE),$(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST)),,\
+      $(if $(filter $(LOCAL_MODULE),$(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)),,\
         $(call pretty-error,The module in product partition cannot be signed with certificate in system.))
     endif
   endif
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 9818d60..d604480 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -452,12 +452,33 @@
 
 # Set up phony targets that covers all modules under the given paths.
 # This allows us to build everything in given paths by running mmma/mma.
-my_path_components := $(subst /,$(space),$(LOCAL_PATH))
-my_path_prefix := MODULES-IN
-$(foreach c, $(my_path_components),\
-  $(eval my_path_prefix := $(my_path_prefix)-$(c))\
-  $(eval .PHONY : $(my_path_prefix))\
-  $(eval $(my_path_prefix) : $(my_all_targets)))
+define my_path_comp
+parent := $(patsubst %/,%,$(dir $(1)))
+parent_target := MODULES-IN-$$(subst /,-,$$(parent))
+.PHONY: $$(parent_target)
+$$(parent_target): $(2)
+ifndef $$(parent_target)
+  $$(parent_target) := true
+  ifneq (,$$(findstring /,$$(parent)))
+    $$(eval $$(call my_path_comp,$$(parent),$$(parent_target)))
+  endif
+endif
+endef
+
+_local_path := $(patsubst %/,%,$(LOCAL_PATH))
+_local_path_target := MODULES-IN-$(subst /,-,$(_local_path))
+
+.PHONY: $(_local_path_target)
+$(_local_path_target): $(my_register_name)
+
+ifndef $(_local_path_target)
+  $(_local_path_target) := true
+  $(eval $(call my_path_comp,$(_local_path),$(_local_path_target)))
+endif
+
+_local_path :=
+_local_path_target :=
+my_path_comp :=
 
 ###########################################################
 ## Module installation rule
diff --git a/core/binary.mk b/core/binary.mk
index 4894bf2..a70a047 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -71,8 +71,12 @@
   my_pool := $(GOMA_OR_RBE_POOL)
 endif
 
-ifneq (,$(strip $(foreach dir,$(COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
-ifeq (,$(strip $(foreach dir,$(COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
+# TODO(b/158212027): Remove `$(COVERAGE_PATHS)` from this condition when all users have been moved
+# to `NATIVE_COVERAGE_PATHS`.
+ifneq (,$(strip $(foreach dir,$(COVERAGE_PATHS) $(NATIVE_COVERAGE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
+# TODO(b/158212027): Remove `$(COVERAGE_EXCLUDE_PATHS)` from this condition when all users have been
+# moved to `NATIVE_COVERAGE_EXCLUDE_PATHS`.
+ifeq (,$(strip $(foreach dir,$(COVERAGE_EXCLUDE_PATHS) $(NATIVE_COVERAGE_EXCLUDE_PATHS),$(filter $(dir)%,$(LOCAL_PATH)))))
   my_native_coverage := true
 else
   my_native_coverage := false
@@ -84,7 +88,7 @@
   my_native_coverage := false
 endif
 
-# Exclude directories from manual binder interface whitelisting.
+# Exclude directories from checking allowed manual binder interface lists.
 # TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths.
 ifneq (,$(filter $(addsuffix %,$(ALLOWED_MANUAL_INTERFACE_PATHS)),$(LOCAL_PATH)))
   my_cflags += -DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES
@@ -282,8 +286,7 @@
 endif
 
 ifneq ($(LOCAL_SDK_VERSION),)
-  my_all_ndk_libraries := \
-      $(NDK_MIGRATED_LIBS) $(addprefix lib,$(NDK_PREBUILT_SHARED_LIBRARIES))
+  my_all_ndk_libraries := $(NDK_KNOWN_LIBS)
   my_ndk_shared_libraries := \
       $(filter $(my_all_ndk_libraries),\
         $(my_shared_libraries) $(my_system_shared_libraries))
@@ -1350,7 +1353,7 @@
 # lists and use addprefix.
 my_ndk_shared_libraries_fullpath := \
     $(foreach _lib,$(my_ndk_shared_libraries),\
-        $(if $(filter $(NDK_MIGRATED_LIBS),$(_lib)),\
+        $(if $(filter $(NDK_KNOWN_LIBS),$(_lib)),\
             $(my_built_ndk_libs)/$(_lib)$(so_suffix),\
             $(my_ndk_sysroot_lib)/$(_lib)$(so_suffix)))
 
@@ -1551,7 +1554,7 @@
 my_allowed_ldlibs :=
 ifndef LOCAL_IS_HOST_MODULE
   ifneq ($(LOCAL_SDK_VERSION),)
-    my_allowed_ldlibs := $(addprefix -l,$(NDK_PREBUILT_SHARED_LIBRARIES))
+    my_allowed_ldlibs := $(NDK_KNOWN_LIBS:lib%=-l%)
   endif
 else
   my_allowed_ldlibs := $($(my_prefix)AVAILABLE_LIBRARIES)
diff --git a/core/board_config.mk b/core/board_config.mk
index 0ff28c5..a6e586d 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -93,6 +93,7 @@
   BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW \
   BUILD_BROKEN_USES_NETWORK \
   BUILD_BROKEN_VINTF_PRODUCT_COPY_FILES \
+  BUILD_BROKEN_DUP_SYSPROP \
 
 _build_broken_var_list += \
   $(foreach m,$(AVAILABLE_BUILD_MODULE_TYPES) \
@@ -242,13 +243,8 @@
 # build a list out of the TARGET_CPU_ABIs specified by the config.
 # Add NATIVE_BRIDGE_ABIs at the end to keep order of preference.
 ifeq (,$(TARGET_CPU_ABI_LIST))
-  ifeq ($(TARGET_IS_64_BIT)|$(TARGET_PREFER_32_BIT_APPS),true|true)
-    TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_32_BIT) $(TARGET_CPU_ABI_LIST_64_BIT) \
-                           $(_target_native_bridge_abi_list_32_bit) $(_target_native_bridge_abi_list_64_bit)
-  else
-    TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_64_BIT) $(TARGET_CPU_ABI_LIST_32_BIT) \
-                           $(_target_native_bridge_abi_list_64_bit) $(_target_native_bridge_abi_list_32_bit)
-  endif
+  TARGET_CPU_ABI_LIST := $(TARGET_CPU_ABI_LIST_64_BIT) $(TARGET_CPU_ABI_LIST_32_BIT) \
+                         $(_target_native_bridge_abi_list_64_bit) $(_target_native_bridge_abi_list_32_bit)
 endif
 
 # Add NATIVE_BRIDGE_ABIs at the end of 32 and 64 bit CPU_ABIs to keep order of preference.
@@ -598,6 +594,9 @@
 endef
 
 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))
+  endif
   ifneq ($(BOARD_VNDK_VERSION),current)
     $(call check_vndk_version,$(BOARD_VNDK_VERSION))
   endif
@@ -622,7 +621,7 @@
   endif
 endif
 
-ifeq (,$(TARGET_BUILD_APPS))
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
 ifdef PRODUCT_EXTRA_VNDK_VERSIONS
   $(foreach v,$(PRODUCT_EXTRA_VNDK_VERSIONS),$(call check_vndk_version,$(v)))
 endif
diff --git a/core/cc_prebuilt_internal.mk b/core/cc_prebuilt_internal.mk
index 99b7d0f..e8e01d8 100644
--- a/core/cc_prebuilt_internal.mk
+++ b/core/cc_prebuilt_internal.mk
@@ -65,7 +65,7 @@
   built_module := $(linked_module)
 
   ifneq ($(LOCAL_SDK_VERSION),)
-    # binary.mk filters out NDK_MIGRATED_LIBS from my_shared_libs, thus those NDK libs are not added
+    # binary.mk filters out NDK_KNOWN_LIBS from my_shared_libs, thus those NDK libs are not added
     # to DEPENDENCIES_ON_SHARED_LIBRARIES. Assign $(my_ndk_shared_libraries_fullpath) to
     # my_check_elf_file_shared_lib_files so that check_elf_file.py can see those NDK stub libs.
     my_check_elf_file_shared_lib_files := $(my_ndk_shared_libraries_fullpath)
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index c88a1cd..20319a8 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -22,6 +22,7 @@
 LOCAL_APIDIFF_OLDAPI:=
 LOCAL_APK_LIBRARIES:=
 LOCAL_APK_SET_MASTER_FILE:=
+LOCAL_APKCERTS_FILE:=
 LOCAL_ARM_MODE:=
 LOCAL_ASFLAGS:=
 LOCAL_ASSET_DIR:=
diff --git a/core/config.mk b/core/config.mk
index 5be1b86..a5b8ef7 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -22,6 +22,13 @@
 
 include $(BUILD_SYSTEM_COMMON)/core.mk
 
+# -----------------------------------------------------------------
+# Rules and functions to help copy important files to DIST_DIR
+# when requested. This must be included once only, and must be included before
+# soong_config (as soong_config calls make_vars-$(TARGET).mk, and soong may
+# propagate calls to dist-for-goals there).
+include $(BUILD_SYSTEM)/distdir.mk
+
 # Mark variables that should be coming as environment variables from soong_ui
 # as readonly
 .KATI_READONLY := OUT_DIR TMPDIR BUILD_DATETIME_FILE
@@ -141,6 +148,9 @@
   TARGET_PROJECT_SYSTEM_INCLUDES \
   2ND_TARGET_PROJECT_SYSTEM_INCLUDES \
   ,Project include variables have been removed)
+$(KATI_obsolete_var TARGET_PREFER_32_BIT TARGET_PREFER_32_BIT_APPS TARGET_PREFER_32_BIT_EXECUTABLES)
+$(KATI_obsolete_var PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST.)
+$(KATI_obsolete_var PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST,Use PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST.)
 
 # Used to force goals to build.  Only use for conditionally defined goals.
 .PHONY: FORCE
@@ -375,11 +385,6 @@
 include $(BUILD_SYSTEM)/rbe.mk
 endif
 
-ifdef TARGET_PREFER_32_BIT
-TARGET_PREFER_32_BIT_APPS := true
-TARGET_PREFER_32_BIT_EXECUTABLES := true
-endif
-
 # GCC version selection
 TARGET_GCC_VERSION := 4.9
 ifdef TARGET_2ND_ARCH
@@ -671,9 +676,9 @@
 # Path to tools.jar
 HOST_JDK_TOOLS_JAR := $(ANDROID_JAVA8_HOME)/lib/tools.jar
 
-APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner --compatible-output=yes
+APICHECK_COMMAND := $(JAVA) -Xmx4g -jar $(APICHECK) --no-banner --compatible-output=no
 
-# Boolean variable determining if the whitelist for compatible properties is enabled
+# Boolean variable determining if the allow list for compatible properties is enabled
 PRODUCT_COMPATIBLE_PROPERTY := false
 ifneq ($(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE),)
   PRODUCT_COMPATIBLE_PROPERTY := $(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE)
@@ -973,8 +978,7 @@
     BOARD_SUPER_PARTITION_GROUPS and BOARD_*_PARTITION_LIST)
 endif
 BOARD_SUPER_PARTITION_PARTITION_LIST := \
-    $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \
-        $(BOARD_$(group)_PARTITION_LIST))
+    $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)),$(BOARD_$(group)_PARTITION_LIST))
 .KATI_READONLY := BOARD_SUPER_PARTITION_PARTITION_LIST
 
 ifneq ($(BOARD_SUPER_PARTITION_SIZE),)
diff --git a/core/construct_context.sh b/core/construct_context.sh
deleted file mode 100755
index d620d08..0000000
--- a/core/construct_context.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2018 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.
-
-set -e
-
-# target_sdk_version: parsed from manifest
-#
-# outputs
-# class_loader_context_arg: final class loader conext arg
-# stored_class_loader_context_arg: final stored class loader context arg
-
-if [ -z "${target_sdk_version}" ]; then
-    echo "ERROR: target_sdk_version not set"
-    exit 2
-fi
-
-# The hidl.manager shared library has a dependency on hidl.base. We'll manually
-# add that information to the class loader context if we see those libraries.
-hidl_manager="android.hidl.manager-V1.0-java"
-hidl_base="android.hidl.base-V1.0-java"
-
-function add_to_contexts {
-  for i in $1; do
-    if [[ -z "${class_loader_context}" ]]; then
-      export class_loader_context="PCL[$i]"
-    else
-      export class_loader_context+="#PCL[$i]"
-    fi
-    if [[ $i == *"$hidl_manager"* ]]; then
-      export class_loader_context+="{PCL[${i/$hidl_manager/$hidl_base}]}"
-    fi
-  done
-
-  for i in $2; do
-    if [[ -z "${stored_class_loader_context}" ]]; then
-      export stored_class_loader_context="PCL[$i]"
-    else
-      export stored_class_loader_context+="#PCL[$i]"
-    fi
-    if [[ $i == *"$hidl_manager"* ]]; then
-      export stored_class_loader_context+="{PCL[${i/$hidl_manager/$hidl_base}]}"
-    fi
-  done
-}
-
-# The order below must match what the package manager also computes for
-# class loader context.
-
-if [[ "${target_sdk_version}" -lt "28" ]]; then
-  add_to_contexts "${conditional_host_libs_28}" "${conditional_target_libs_28}"
-fi
-
-if [[ "${target_sdk_version}" -lt "29" ]]; then
-  add_to_contexts "${conditional_host_libs_29}" "${conditional_target_libs_29}"
-fi
-
-if [[ "${target_sdk_version}" -lt "30" ]]; then
-  add_to_contexts "${conditional_host_libs_30}" "${conditional_target_libs_30}"
-fi
-
-add_to_contexts "${dex_preopt_host_libraries}" "${dex_preopt_target_libraries}"
-
-# Generate the actual context string.
-export class_loader_context_arg="--class-loader-context=PCL[]{${class_loader_context}}"
-export stored_class_loader_context_arg="--stored-class-loader-context=PCL[]{${stored_class_loader_context}}"
diff --git a/core/definitions.mk b/core/definitions.mk
index 5c3a074..2bf1ba6 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -539,6 +539,14 @@
 endef
 
 ###########################################################
+## Convert install path to on-device path.
+###########################################################
+# $(1): install path
+define install-path-to-on-device-path
+$(patsubst $(PRODUCT_OUT)%,%,$(1))
+endef
+
+###########################################################
 ## The intermediates directory.  Where object files go for
 ## a given target.  We could technically get away without
 ## the "_intermediates" suffix on the directory, but it's
@@ -3078,11 +3086,6 @@
 ## Other includes
 ###########################################################
 
-# -----------------------------------------------------------------
-# Rules and functions to help copy important files to DIST_DIR
-# when requested.
-include $(BUILD_SYSTEM)/distdir.mk
-
 # Include any vendor specific definitions.mk file
 -include $(TOPDIR)vendor/*/build/core/definitions.mk
 -include $(TOPDIR)device/*/build/core/definitions.mk
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index 56f0a62..55f6f0b 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -1,5 +1,14 @@
 DEX_PREOPT_CONFIG := $(SOONG_OUT_DIR)/dexpreopt.config
 
+ENABLE_PREOPT := true
+ifneq (true,$(filter true,$(WITH_DEXPREOPT)))
+  ENABLE_PREOPT :=
+else ifneq (true,$(filter true,$(PRODUCT_USES_DEFAULT_ART_CONFIG)))
+  ENABLE_PREOPT :=
+else ifneq (,$(TARGET_BUILD_APPS))
+  ENABLE_PREOPT :=
+endif
+
 # The default value for LOCAL_DEX_PREOPT
 DEX_PREOPT_DEFAULT ?= true
 
@@ -45,10 +54,13 @@
 DIRTY_IMAGE_OBJECTS := $(call word-colon,1,$(firstword \
     $(filter %system/etc/dirty-image-objects,$(PRODUCT_COPY_FILES))))
 
+# Get value of a property. It is first searched from PRODUCT_VENDOR_PROPERTIES
+# and then falls back to PRODUCT_SYSTEM_PROPERTIES
+# $1: name of the property
 define get-product-default-property
 $(strip \
-  $(eval _prop := $(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES))))\
-  $(if $(_prop),$(_prop),$(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_SYSTEM_DEFAULT_PROPERTIES)))))
+  $(eval _prop := $(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_VENDOR_PROPERTIES))))\
+  $(if $(_prop),$(_prop),$(patsubst $(1)=%,%,$(filter $(1)=%,$(PRODUCT_SYSTEM_PROPERTIES)))))
 endef
 
 DEX2OAT_IMAGE_XMS := $(call get-product-default-property,dalvik.vm.image-dex2oat-Xms)
@@ -60,7 +72,7 @@
 
   $(call json_start)
 
-  $(call add_json_bool, DisablePreopt,                           $(call invert_bool,$(and $(filter true,$(PRODUCT_USES_DEFAULT_ART_CONFIG)),$(filter true,$(WITH_DEXPREOPT)))))
+  $(call add_json_bool, DisablePreopt,                           $(call invert_bool,$(ENABLE_PREOPT)))
   $(call add_json_list, DisablePreoptModules,                    $(DEXPREOPT_DISABLED_MODULES))
   $(call add_json_bool, OnlyPreoptBootImageAndSystemServer,      $(filter true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY)))
   $(call add_json_bool, UseArtImage,                             $(filter true,$(DEXPREOPT_USE_ART_IMAGE)))
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 6300e78..f738c3e 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -189,8 +189,8 @@
   my_filtered_optional_uses_libraries := $(filter-out $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES), \
     $(LOCAL_OPTIONAL_USES_LIBRARIES))
 
-  # dexpreopt needs the paths to the dex jars of these libraries in case
-  # construct_context.sh needs to pass them to dex2oat.
+  # dexpreopt needs the paths to the dex jars of these libraries in order to
+  # construct class loader context for dex2oat.
   my_extra_dexpreopt_libs := \
     org.apache.http.legacy \
     android.hidl.base-V1.0-java \
@@ -235,8 +235,9 @@
   $(call add_json_map,  LibraryPaths)
   $(foreach lib,$(my_dexpreopt_libs),\
     $(call add_json_map, $(lib)) \
-    $(call add_json_str, Host, $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \
-    $(call add_json_str, Device, /system/framework/$(lib).jar) \
+    $(eval file := $(filter %/$(lib).jar, $(call module-installed-files,$(lib)))) \
+    $(call add_json_str, Host,   $(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)/javalib.jar) \
+    $(call add_json_str, Device, $(call install-path-to-on-device-path,$(file))) \
     $(call end_json_map))
   $(call end_json_map)
   $(call add_json_list, Archs,                          $(my_dexpreopt_archs))
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 782ee5f..3aff007 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -94,10 +94,24 @@
 
 TARGET_BUILD_APPS ?=
 
+# Set to true for an unbundled build, i.e. a build without
+# support for platform targets like the system image. This also
+# disables consistency checks that only apply to full platform
+# builds.
+TARGET_BUILD_UNBUNDLED ?=
+
+# TARGET_BUILD_APPS implies unbundled build, otherwise we default
+# to bundled (i.e. platform targets such as the system image are
+# included).
+ifneq ($(TARGET_BUILD_APPS),)
+  TARGET_BUILD_UNBUNDLED := true
+endif
+
 .KATI_READONLY := \
   TARGET_PRODUCT \
   TARGET_BUILD_VARIANT \
-  TARGET_BUILD_APPS
+  TARGET_BUILD_APPS \
+  TARGET_BUILD_UNBUNDLED \
 
 # ---------------------------------------------------------------
 # Set up configuration for host machine.  We don't do cross-
diff --git a/core/executable.mk b/core/executable.mk
index db8dcc6..9175e0a 100644
--- a/core/executable.mk
+++ b/core/executable.mk
@@ -40,14 +40,9 @@
 LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true
 endif
 
-# if TARGET_PREFER_32_BIT_EXECUTABLES is set, try to build 32-bit first
 ifdef TARGET_2ND_ARCH
-ifeq ($(TARGET_PREFER_32_BIT_EXECUTABLES),true)
-LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)
-else
 LOCAL_2ND_ARCH_VAR_PREFIX :=
 endif
-endif
 
 my_skip_non_preferred_arch :=
 
@@ -65,12 +60,7 @@
 ifndef my_skip_non_preferred_arch
 ifdef TARGET_2ND_ARCH
 
-# check if the non-preferred arch is the primary or secondary
-ifeq ($(TARGET_PREFER_32_BIT_EXECUTABLES),true)
-LOCAL_2ND_ARCH_VAR_PREFIX :=
-else
 LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)
-endif
 
 # check if non-preferred arch is supported
 include $(BUILD_SYSTEM)/module_arch_supported.mk
diff --git a/core/executable_prefer_symlink.mk b/core/executable_prefer_symlink.mk
index ad6698d..fea0bef 100644
--- a/core/executable_prefer_symlink.mk
+++ b/core/executable_prefer_symlink.mk
@@ -5,22 +5,13 @@
 # Note: now only limited to the binaries that will be installed under system/bin directory
 
 # Create link to the one used depending on the target
-# configuration. Note that we require the TARGET_IS_64_BIT
-# check because 32 bit targets may not define TARGET_PREFER_32_BIT_APPS
-# et al. since those variables make no sense in that context.
+# configuration.
 ifneq ($(LOCAL_IS_HOST_MODULE),true)
   my_symlink := $(addprefix $(TARGET_OUT)/bin/, $(LOCAL_MODULE))
   my_src_binary_name :=
   ifeq ($(TARGET_IS_64_BIT),true)
     ifeq ($(TARGET_SUPPORTS_64_BIT_APPS)|$(TARGET_SUPPORTS_32_BIT_APPS),true|true)
-      # We support both 32 and 64 bit apps, so we will have to
-      # base our decision on whether the target prefers one or the
-      # other.
-      ifeq ($(TARGET_PREFER_32_BIT_APPS),true)
-        my_src_binary_name := $(LOCAL_MODULE_STEM_32)
-      else
-        my_src_binary_name := $(LOCAL_MODULE_STEM_64)
-      endif
+      my_src_binary_name := $(LOCAL_MODULE_STEM_64)
     else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
       # We support only 64 bit apps.
       my_src_binary_name := $(LOCAL_MODULE_STEM_64)
diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk
index 30bcc2c..289d16f 100644
--- a/core/install_jni_libs_internal.mk
+++ b/core/install_jni_libs_internal.mk
@@ -57,9 +57,9 @@
   # Make sure the JNI libraries get installed
   my_shared_library_path := $(call get_non_asan_path,\
       $($(my_2nd_arch_prefix)TARGET_OUT$(partition_tag)_SHARED_LIBRARIES))
-  my_installed_library := $(addprefix $(my_shared_library_path)/, $(my_jni_filenames))
 
-  ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed_library)
+  bit_suffix := $(if $(filter %64,$(TARGET_$(my_2nd_arch_prefix)ARCH)),:64,:32)
+  ALL_MODULES.$(my_register_name).REQUIRED_FROM_TARGET += $(addsuffix $(bit_suffix),$(LOCAL_JNI_SHARED_LIBRARIES))
 
   # Create symlink in the app specific lib path
   # Skip creating this symlink when running the second part of a target sanitization build.
diff --git a/core/java.mk b/core/java.mk
index 9d42775..2f18ad9 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -393,7 +393,7 @@
 else
   # For platform build, we can't just raise to the "current" SDK,
   # that would break apps that use APIs removed from the current SDK.
-  my_proguard_sdk_raise := $(call java-lib-header-files,$(TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES) $(TARGET_DEFAULT_JAVA_LIBRARIES))
+  my_proguard_sdk_raise := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES))
 endif
 ifdef BOARD_SYSTEMSDK_VERSIONS
 ifneq (,$(filter true,$(LOCAL_VENDOR_MODULE) $(LOCAL_ODM_MODULE) $(LOCAL_PROPRIETARY_MODULE)))
diff --git a/core/java_common.mk b/core/java_common.mk
index b218c0d..658296d 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -265,11 +265,11 @@
       # Most users of LOCAL_NO_STANDARD_LIBRARIES really mean no framework libs,
       # and manually add back the core libs.  The ones that don't are in soong
       # now, so just always assume that they want the default system modules
-      my_system_modules := $(DEFAULT_SYSTEM_MODULES)
+      my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)
     else  # LOCAL_NO_STANDARD_LIBRARIES
-      full_java_bootclasspath_libs := $(call java-lib-header-files,$(TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES) $(TARGET_DEFAULT_JAVA_LIBRARIES))
-      LOCAL_JAVA_LIBRARIES := $(filter-out $(TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES) $(TARGET_DEFAULT_JAVA_LIBRARIES),$(LOCAL_JAVA_LIBRARIES))
-      my_system_modules := $(DEFAULT_SYSTEM_MODULES)
+      full_java_bootclasspath_libs := $(call java-lib-header-files,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES))
+      LOCAL_JAVA_LIBRARIES := $(filter-out $(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES) $(FRAMEWORK_LIBRARIES),$(LOCAL_JAVA_LIBRARIES))
+      my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)
     endif  # LOCAL_NO_STANDARD_LIBRARIES
 
     ifneq (,$(TARGET_BUILD_APPS_USE_PREBUILT_SDK))
@@ -352,10 +352,10 @@
     ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)
       empty_bootclasspath := ""
     else
-      full_java_bootclasspath_libs := $(call java-lib-header-files,$(addsuffix -hostdex,$(TARGET_DEFAULT_BOOTCLASSPATH_LIBRARIES)),true)
+      full_java_bootclasspath_libs := $(call java-lib-header-files,$(addsuffix -hostdex,$(LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES)),true)
     endif
 
-    my_system_modules := $(DEFAULT_SYSTEM_MODULES)
+    my_system_modules := $(LEGACY_CORE_PLATFORM_SYSTEM_MODULES)
     full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),true)
     full_shared_java_header_libs := $(call java-lib-header-files,$(LOCAL_JAVA_LIBRARIES),true)
   else # !USE_CORE_LIB_BOOTCLASSPATH
diff --git a/core/line_coverage.mk b/core/line_coverage.mk
index 9b0b528..babcb30 100644
--- a/core/line_coverage.mk
+++ b/core/line_coverage.mk
@@ -8,8 +8,7 @@
 # packs them into another zip file called `line_coverage_profiles.zip`.
 #
 # To run the make target set the coverage related envvars first:
-# 	NATIVE_LINE_COVERAGE=true NATIVE_COVERAGE=true \
-#	COVERAGE_PATHS=* make haiku-line-coverage
+# 	NATIVE_COVERAGE=true NATIVE_COVERAGE_PATHS=* make haiku-line-coverage
 # -----------------------------------------------------------------
 
 # TODO(b/148306195): Due this issue some fuzz targets cannot be built with
@@ -47,7 +46,6 @@
 	libinputflinger \
 	libopus \
 	libstagefright \
-	libunwind \
 	libvixl:com.android.art.debug
 
 # Use the intermediates directory to avoid installing libraries to the device.
@@ -68,7 +66,7 @@
 fuzz_target_inputs := $(foreach fuzz,$(fuzz_targets), \
 	$(call intermediates-dir-for,EXECUTABLES,$(fuzz))/$(fuzz))
 
-# When line coverage is enabled (NATIVE_LINE_COVERAGE is set), make creates
+# When coverage is enabled (NATIVE_COVERAGE is set), make creates
 # a "coverage" directory and stores all profile (*.gcno) files in inside.
 # We need everything that is stored inside this directory.
 $(line_coverage_profiles): $(fuzz_target_inputs)
diff --git a/core/main.mk b/core/main.mk
index 79bc9a5..114ec55 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -102,6 +102,15 @@
 EMMA_INSTRUMENT := true
 endif
 
+# TODO(b/158212027): Turn this into an error when all users have been moved to
+# `NATIVE_COVERAGE_PATHS` and `NATIVE_COVERAGE_EXCLUDE_PATHS`.
+ifneq ($(COVERAGE_PATHS),)
+  $(warning Variable COVERAGE_PATHS is deprecated. Please use NATIVE_COVERAGE_PATHS instead.)
+endif
+ifneq ($(COVERAGE_EXCLUDE_PATHS),)
+  $(warning Variable COVERAGE_EXCLUDE_PATHS is deprecated. Please use NATIVE_COVERAGE_EXCLUDE_PATHS instead.)
+endif
+
 ifeq (true,$(EMMA_INSTRUMENT))
 # Adding the jacoco library can cause the inclusion of
 # some typically banned classes
@@ -198,8 +207,8 @@
 	variables like PRODUCT_SEPOLICY_SPLIT should be used until that is \
 	possible.)
 
-# Sets ro.actionable_compatible_property.enabled to know on runtime whether the whitelist
-# of actionable compatible properties is enabled or not.
+# Sets ro.actionable_compatible_property.enabled to know on runtime whether the
+# allowed list of actionable compatible properties is enabled or not.
 ifeq ($(PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE),true)
 ADDITIONAL_SYSTEM_PROPERTIES += ro.actionable_compatible_property.enabled=false
 else
@@ -242,8 +251,6 @@
   ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION)
   ADDITIONAL_VENDOR_PROPERTIES += ro.vndk.lite=true
 endif
-ADDITIONAL_VENDOR_PROPERTIES += \
-    $(call collapse-pairs, $(PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
 
 # Add cpu properties for bionic and ART.
 ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH)
@@ -508,7 +515,7 @@
 # Include all of the makefiles in the system
 #
 
-subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
+subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) $(SOONG_OUT_DIR)/late-$(TARGET_PRODUCT).mk
 subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
 .KATI_READONLY := subdir_makefiles_total
 
@@ -635,9 +642,11 @@
 endef
 
 # TODO(b/7456955): error if a required module doesn't exist.
-# Resolve the required module names in ALL_MODULES.*.REQUIRED_FROM_TARGET,
-# ALL_MODULES.*.REQUIRED_FROM_HOST and ALL_MODULES.*.REQUIRED_FROM_HOST_CROSS
-# to 32-bit or 64-bit variant.
+# Resolve the required module names to 32-bit or 64-bit variant for:
+#   ALL_MODULES.<*>.REQUIRED_FROM_TARGET
+#   ALL_MODULES.<*>.REQUIRED_FROM_HOST
+#   ALL_MODULES.<*>.REQUIRED_FROM_HOST_CROSS
+#
 # If a module is for cross host OS, the required modules are also for that OS.
 # Required modules explicitly suffixed with :32 or :64 resolve to that bitness.
 # Otherwise if the requiring module is native and the required module is shared
@@ -653,27 +662,53 @@
       $(eval r := $(addprefix host_cross_,$(r)))) \
     $(eval module_is_native := \
       $(filter EXECUTABLES SHARED_LIBRARIES NATIVE_TESTS,$(ALL_MODULES.$(m).CLASS))) \
-    $(eval r_r := $(foreach r_i,$(r), \
-      $(if $(filter %:32 %:64,$(r_i)), \
-        $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))), \
-        $(eval r_m := \
-          $(eval r_i_2nd := $(call get-modules-for-2nd-arch,$(1),$(r_i))) \
-          $(eval required_is_shared_library_or_native_test := \
-            $(filter SHARED_LIBRARIES NATIVE_TESTS, \
-              $(ALL_MODULES.$(r_i).CLASS) $(ALL_MODULES.$(r_i_2nd).CLASS))) \
-          $(if $(and $(module_is_native),$(required_is_shared_library_or_native_test)), \
-            $(if $(ALL_MODULES.$(m).FOR_2ND_ARCH),$(r_i_2nd),$(r_i)), \
-            $(r_i) $(r_i_2nd)))) \
-      $(eval ### TODO(b/7456955): error if r_m is empty / does not exist) \
-      $(r_m))) \
+    $(eval r_r := \
+      $(foreach r_i,$(r), \
+        $(if $(filter %:32 %:64,$(r_i)), \
+          $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))), \
+          $(eval r_m := \
+            $(eval r_i_2nd := $(call get-modules-for-2nd-arch,$(1),$(r_i))) \
+            $(eval required_is_shared_library_or_native_test := \
+              $(filter SHARED_LIBRARIES NATIVE_TESTS, \
+                $(ALL_MODULES.$(r_i).CLASS) $(ALL_MODULES.$(r_i_2nd).CLASS))) \
+            $(if $(and $(module_is_native),$(required_is_shared_library_or_native_test)), \
+              $(if $(ALL_MODULES.$(m).FOR_2ND_ARCH),$(r_i_2nd),$(r_i)), \
+              $(r_i) $(r_i_2nd)))) \
+        $(eval ### TODO(b/7456955): error if r_m is empty / does not exist) \
+        $(r_m))) \
     $(eval ALL_MODULES.$(m).REQUIRED_FROM_$(1) := $(sort $(r_r))) \
   ) \
 )
 endef
 
+# Resolve the required module names to 32-bit or 64-bit variant for:
+#   ALL_MODULES.<*>.TARGET_REQUIRED_FROM_HOST
+#   ALL_MODULES.<*>.HOST_REQUIRED_FROM_TARGET
+#
+# This is like select-bitness-of-required-modules, but it doesn't have
+# complicated logic for various module types.
+# Calls resolve-bitness-for-modules to resolve module names.
+# $(1): TARGET or HOST
+# $(2): HOST or TARGET
+define select-bitness-of-target-host-required-modules
+$(foreach m,$(ALL_MODULES), \
+  $(eval r := $(ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2))) \
+  $(if $(r), \
+    $(eval r_r := \
+      $(foreach r_i,$(r), \
+        $(eval r_m := $(call resolve-bitness-for-modules,$(1),$(r_i))) \
+        $(eval ### TODO(b/7456955): error if r_m is empty / does not exist) \
+        $(r_m))) \
+    $(eval ALL_MODULES.$(m).$(1)_REQUIRED_FROM_$(2) := $(sort $(r_r))) \
+  ) \
+)
+endef
+
 $(call select-bitness-of-required-modules,TARGET)
 $(call select-bitness-of-required-modules,HOST)
 $(call select-bitness-of-required-modules,HOST_CROSS)
+$(call select-bitness-of-target-host-required-modules,TARGET,HOST)
+$(call select-bitness-of-target-host-required-modules,HOST,TARGET)
 
 define add-required-deps
 $(1): | $(2)
@@ -744,10 +779,13 @@
     $(eval req_files := )\
     $(foreach req_mod,$(req_mods), \
       $(eval req_file := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(req_mod)))) \
-      $(if $(strip $(req_file)),\
-        ,\
-        $(error $(m).LOCAL_TARGET_REQUIRED_MODULES : illegal value $(req_mod) : not a device module. If you want to specify host modules to be required to be installed along with your host module, add those module names to LOCAL_REQUIRED_MODULES instead)\
-      )\
+      $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \
+        , \
+        $(if $(strip $(req_file)), \
+          , \
+          $(error $(m).LOCAL_TARGET_REQUIRED_MODULES : illegal value $(req_mod) : not a device module. If you want to specify host modules to be required to be installed along with your host module, add those module names to LOCAL_REQUIRED_MODULES instead) \
+        ) \
+      ) \
       $(eval req_files := $(req_files)$(space)$(req_file))\
     )\
     $(eval req_files := $(strip $(req_files)))\
@@ -770,10 +808,13 @@
     $(eval req_files := )\
     $(foreach req_mod,$(req_mods), \
       $(eval req_file := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(req_mod)))) \
-      $(if $(strip $(req_file)),\
-        ,\
-        $(error $(m).LOCAL_HOST_REQUIRED_MODULES : illegal value $(req_mod) : not a host module. If you want to specify target modules to be required to be installed along with your target module, add those module names to LOCAL_REQUIRED_MODULES instead)\
-      )\
+      $(if $(filter true,$(ALLOW_MISSING_DEPENDENCIES)), \
+        , \
+        $(if $(strip $(req_file)), \
+          , \
+          $(error $(m).LOCAL_HOST_REQUIRED_MODULES : illegal value $(req_mod) : not a host module. If you want to specify target modules to be required to be installed along with your target module, add those module names to LOCAL_REQUIRED_MODULES instead) \
+        ) \
+      ) \
       $(eval req_files := $(req_files)$(space)$(req_file))\
     )\
     $(eval req_files := $(strip $(req_files)))\
@@ -1190,7 +1231,7 @@
   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))
-      _whitelist := $(PRODUCT_ENFORCE_PACKAGES_EXIST_WHITELIST)
+      _allow_list := $(PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST)
       _modules := $(PRODUCT_PACKAGES)
       # Strip :32 and :64 suffixes
       _modules := $(patsubst %:32,%,$(_modules))
@@ -1199,10 +1240,10 @@
       # existence if either <module> or the <module>_32 variant.
       _nonexistent_modules := $(foreach m,$(_modules), \
         $(if $(or $(ALL_MODULES.$(m).PATH),$(call get-modules-for-2nd-arch,TARGET,$(m))),,$(m)))
-      $(call maybe-print-list-and-error,$(filter-out $(_whitelist),$(_nonexistent_modules)),\
+      $(call maybe-print-list-and-error,$(filter-out $(_allow_list),$(_nonexistent_modules)),\
         $(INTERNAL_PRODUCT) includes non-existent modules in PRODUCT_PACKAGES)
-      $(call maybe-print-list-and-error,$(filter-out $(_nonexistent_modules),$(_whitelist)),\
-        $(INTERNAL_PRODUCT) includes redundant whitelist entries for non-existent PRODUCT_PACKAGES)
+      $(call maybe-print-list-and-error,$(filter-out $(_nonexistent_modules),$(_allow_list)),\
+        $(INTERNAL_PRODUCT) includes redundant allow list entries for non-existent PRODUCT_PACKAGES)
     endif
 
     # Check to ensure that all modules in PRODUCT_HOST_PACKAGES exist
@@ -1221,8 +1262,8 @@
     endif
   endif
 
-  # Some modules produce only host installed files when building with TARGET_BUILD_APPS
-  ifeq ($(TARGET_BUILD_APPS),)
+  # Modules may produce only host installed files in unbundled builds.
+  ifeq (,$(TARGET_BUILD_UNBUNDLED))
     _modules := $(call resolve-bitness-for-modules,TARGET, \
       $(PRODUCT_PACKAGES) \
       $(PRODUCT_PACKAGES_DEBUG) \
@@ -1246,21 +1287,21 @@
   is_asan := $(if $(filter address,$(SANITIZE_TARGET)),true)
   ifneq (true,$(or $(is_asan),$(DISABLE_ARTIFACT_PATH_REQUIREMENTS)))
   # Fakes don't get installed, and NDK stubs aren't installed to device.
-  static_whitelist_patterns := $(TARGET_OUT_FAKE)/% $(SOONG_OUT_DIR)/ndk/%
+  static_allowed_patterns := $(TARGET_OUT_FAKE)/% $(SOONG_OUT_DIR)/ndk/%
   # RROs become REQUIRED by the source module, but are always placed on the vendor partition.
-  static_whitelist_patterns += %__auto_generated_rro_product.apk
-  static_whitelist_patterns += %__auto_generated_rro_vendor.apk
+  static_allowed_patterns += %__auto_generated_rro_product.apk
+  static_allowed_patterns += %__auto_generated_rro_vendor.apk
   # Auto-included targets are not considered
-  static_whitelist_patterns += $(call product-installed-files,)
+  static_allowed_patterns += $(call product-installed-files,)
   # $(PRODUCT_OUT)/apex is where shared libraries in APEXes get installed.
   # The path can be considered as a fake path, as the shared libraries
   # are installed there just to have symbols files for them under
   # $(PRODUCT_OUT)/symbols/apex for debugging purpose. The /apex directory
   # is never compiled into a filesystem image.
-  static_whitelist_patterns += $(PRODUCT_OUT)/apex/%
+  static_allowed_patterns += $(PRODUCT_OUT)/apex/%
   ifeq (true,$(BOARD_USES_SYSTEM_OTHER_ODEX))
     # Allow system_other odex space optimization.
-    static_whitelist_patterns += \
+    static_allowed_patterns += \
       $(TARGET_OUT_SYSTEM_OTHER)/%.odex \
       $(TARGET_OUT_SYSTEM_OTHER)/%.vdex \
       $(TARGET_OUT_SYSTEM_OTHER)/%.art
@@ -1276,31 +1317,31 @@
   $(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\
     $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \
     $(eval ### Verify that the product only produces files inside its path requirements.) \
-    $(eval whitelist := $(PRODUCTS.$(makefile).ARTIFACT_PATH_WHITELIST)) \
+    $(eval allowed := $(PRODUCTS.$(makefile).ARTIFACT_PATH_ALLOWED_LIST)) \
     $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \
-    $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
+    $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \
     $(eval files := $(call product-installed-files, $(makefile))) \
-    $(eval offending_files := $(filter-out $(path_patterns) $(whitelist_patterns) $(static_whitelist_patterns),$(files))) \
+    $(eval offending_files := $(filter-out $(path_patterns) $(allowed_patterns) $(static_allowed_patterns),$(files))) \
     $(call maybe-print-list-and-error,$(offending_files),\
       $(makefile) produces files outside its artifact path requirement. \
       Allowed paths are $(subst $(space),$(comma)$(space),$(addsuffix *,$(requirements)))) \
-    $(eval unused_whitelist := $(filter-out $(files),$(whitelist_patterns))) \
-    $(call maybe-print-list-and-error,$(unused_whitelist),$(makefile) includes redundant whitelist entries in its artifact path requirement.) \
+    $(eval unused_allowed := $(filter-out $(files),$(allowed_patterns))) \
+    $(call maybe-print-list-and-error,$(unused_allowed),$(makefile) includes redundant allowed entries in its artifact path requirement.) \
     $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \
     $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_target_FILES))) \
     $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \
     $(eval all_offending_files += $(files_in_requirement)) \
-    $(eval whitelist := $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST)) \
-    $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
-    $(eval offending_files := $(filter-out $(whitelist_patterns),$(files_in_requirement))) \
+    $(eval allowed := $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST)) \
+    $(eval allowed_patterns := $(call resolve-product-relative-paths,$(allowed))) \
+    $(eval offending_files := $(filter-out $(allowed_patterns),$(files_in_requirement))) \
     $(eval enforcement := $(PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS)) \
     $(if $(enforcement),\
       $(call maybe-print-list-and-error,$(offending_files),\
         $(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement. \
         $(PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT)) \
-      $(eval unused_whitelist := $(if $(filter true strict,$(enforcement)),\
-        $(foreach p,$(whitelist_patterns),$(if $(filter $(p),$(extra_files)),,$(p))))) \
-      $(call maybe-print-list-and-error,$(unused_whitelist),$(INTERNAL_PRODUCT) includes redundant artifact path requirement whitelist entries.) \
+      $(eval unused_allowed := $(if $(filter true strict,$(enforcement)),\
+        $(foreach p,$(allowed_patterns),$(if $(filter $(p),$(extra_files)),,$(p))))) \
+      $(call maybe-print-list-and-error,$(unused_allowed),$(INTERNAL_PRODUCT) includes redundant artifact path requirement allowed list entries.) \
     ) \
   )
 $(PRODUCT_OUT)/offending_artifacts.txt:
@@ -1611,7 +1652,7 @@
     $(apps_only_installed_files)))
 
 
-else # TARGET_BUILD_APPS
+else ifeq (,$(TARGET_BUILD_UNBUNDLED))
   $(call dist-for-goals, droidcore, \
     $(INTERNAL_UPDATE_PACKAGE_TARGET) \
     $(INTERNAL_OTA_PACKAGE_TARGET) \
@@ -1705,10 +1746,15 @@
   $(call dist-for-goals, dist_files, $(api_xmls))
   api_xmls :=
 
+  ifdef CLANG_COVERAGE
+    $(foreach f,$(SOONG_NDK_API_XML), \
+        $(call dist-for-goals,droidcore,$(f):ndk_apis/$(notdir $(f))))
+  endif
+
 # Building a full system-- the default is to build droidcore
 droid_targets: droidcore dist_files
 
-endif # TARGET_BUILD_APPS
+endif # !TARGET_BUILD_UNBUNDLED
 
 .PHONY: docs
 docs: $(ALL_DOCS)
diff --git a/core/package.mk b/core/package.mk
index 6bde485..591f7aa 100644
--- a/core/package.mk
+++ b/core/package.mk
@@ -41,15 +41,7 @@
 endif
 
 LOCAL_NO_2ND_ARCH_MODULE_SUFFIX := true
-
-# if TARGET_PREFER_32_BIT_APPS is set, try to build 32-bit first
-ifdef TARGET_2ND_ARCH
-ifeq ($(TARGET_PREFER_32_BIT_APPS),true)
-LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)
-else
 LOCAL_2ND_ARCH_VAR_PREFIX :=
-endif
-endif
 
 # check if preferred arch is supported
 include $(BUILD_SYSTEM)/module_arch_supported.mk
@@ -57,13 +49,7 @@
 # first arch is supported
 include $(BUILD_SYSTEM)/package_internal.mk
 else ifneq (,$(TARGET_2ND_ARCH))
-# check if the non-preferred arch is the primary or secondary
-ifeq ($(TARGET_PREFER_32_BIT_APPS),true)
-LOCAL_2ND_ARCH_VAR_PREFIX :=
-else
 LOCAL_2ND_ARCH_VAR_PREFIX := $(TARGET_2ND_ARCH_VAR_PREFIX)
-endif
-
 # check if non-preferred arch is supported
 include $(BUILD_SYSTEM)/module_arch_supported.mk
 ifeq ($(my_module_arch_supported),true)
diff --git a/core/product.mk b/core/product.mk
index 94466fa..f531319 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -145,25 +145,27 @@
     PRODUCT_SYSTEM_BRAND \
     PRODUCT_SYSTEM_MANUFACTURER \
 
-# A list of property assignments, like "key = value", with zero or more
-# whitespace characters on either side of the '='.
-_product_list_vars += PRODUCT_PROPERTY_OVERRIDES
+# PRODUCT_<PARTITION>_PROPERTIES are lists of property assignments
+# that go to <partition>/build.prop. Each property assignment is
+# "key = value" with zero or more whitespace characters on either
+# side of the '='.
+_product_list_vars += \
+    PRODUCT_SYSTEM_PROPERTIES \
+    PRODUCT_SYSTEM_EXT_PROPERTIES \
+    PRODUCT_VENDOR_PROPERTIES \
+    PRODUCT_ODM_PROPERTIES \
+    PRODUCT_PRODUCT_PROPERTIES
 
-# A list of property assignments, like "key = value", with zero or more
-# whitespace characters on either side of the '='.
-# used for adding properties to default.prop
+# TODO(b/117892318) deprecate these:
+# ... in favor or PRODUCT_SYSTEM_PROPERTIES
+_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
+# ... in favor of PRODUCT_VENDOR_PROPERTIES
+_product_list_vars += PRODUCT_PROPERTY_OVERRIDES
 _product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES
 
-# A list of property assignments, like "key = value", with zero or more
-# whitespace characters on either side of the '='.
-# used for adding properties to build.prop of product partition
-_product_list_vars += PRODUCT_PRODUCT_PROPERTIES
-
-# A list of property assignments, like "key = value", with zero or more
-# whitespace characters on either side of the '='.
-# used for adding properties to build.prop of system_ext and odm partitions
-_product_list_vars += PRODUCT_SYSTEM_EXT_PROPERTIES
-_product_list_vars += PRODUCT_ODM_PROPERTIES
+# TODO(b/117892318) consider deprecating these too
+_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
+_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST
 
 # The characteristics of the product, which among other things is passed to aapt
 _product_single_value_vars += PRODUCT_CHARACTERISTICS
@@ -226,15 +228,6 @@
 _product_list_vars += PRODUCT_SUPPORTS_VBOOT
 _product_list_vars += PRODUCT_SUPPORTS_VERITY
 _product_list_vars += PRODUCT_SUPPORTS_VERITY_FEC
-_product_list_vars += PRODUCT_OEM_PROPERTIES
-
-# A list of property assignments, like "key = value", with zero or more
-# whitespace characters on either side of the '='.
-# used for adding properties to default.prop of system partition
-_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
-
-_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
-_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST
 _product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
 _product_list_vars += PRODUCT_SYSTEM_SERVER_JARS
 # List of system_server jars delivered via apex. Format = <apex name>:<jar name>.
@@ -328,14 +321,14 @@
 # partitions uses PLATFORM_VNDK_VERSION.
 _product_single_value_var += PRODUCT_PRODUCT_VNDK_VERSION
 
-# Whether the whitelist of actionable compatible properties should be disabled or not
+# Whether the list of allowed of actionable compatible properties should be disabled or not
 _product_single_value_vars += PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE
 
 _product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
 _product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT
-_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST
+_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST
 _product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT
-_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST
+_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
 
 # List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence
 # installed on /system directory by default.
@@ -455,19 +448,19 @@
 define require-artifacts-in-path
   $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \
   $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_REQUIREMENTS := $(strip $(1))) \
-  $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_WHITELIST := $(strip $(2))) \
+  $(eval PRODUCTS.$(current_mk).ARTIFACT_PATH_ALLOWED_LIST := $(strip $(2))) \
   $(eval ARTIFACT_PATH_REQUIREMENT_PRODUCTS := \
     $(sort $(ARTIFACT_PATH_REQUIREMENT_PRODUCTS) $(current_mk)))
 endef
 
 # Makes including non-existent modules in PRODUCT_PACKAGES an error.
-# $(1): whitelist of non-existent modules to allow.
+# $(1): list of non-existent modules to allow.
 define enforce-product-packages-exist
   $(eval current_mk := $(strip $(word 1,$(_include_stack)))) \
   $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST := true) \
-  $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_WHITELIST := $(1)) \
+  $(eval PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST := $(1)) \
   $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST) \
-  $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_WHITELIST)
+  $(eval .KATI_READONLY := PRODUCTS.$(current_mk).PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST)
 endef
 
 #
@@ -589,7 +582,7 @@
 $(foreach v,\
   $(_product_var_list) \
     PRODUCT_ENFORCE_PACKAGES_EXIST \
-    PRODUCT_ENFORCE_PACKAGES_EXIST_WHITELIST, \
+    PRODUCT_ENFORCE_PACKAGES_EXIST_ALLOW_LIST, \
   $(eval $(v) := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).$(v)))) \
   $(eval get-product-var = $$(if $$(filter $$(1),$$(INTERNAL_PRODUCT)),$$($$(2)),$$(PRODUCTS.$$(strip $$(1)).$$(2)))) \
   $(KATI_obsolete_var PRODUCTS.$(INTERNAL_PRODUCT).$(v),Use $(v) instead) \
diff --git a/core/product_config.mk b/core/product_config.mk
index 82967bc..7a7c4b7 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -280,7 +280,7 @@
     $(error A jar in PRODUCT_UPDATABLE_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
 
 ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)
-ENFORCE_SYSTEM_CERTIFICATE_WHITELIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST)
+ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)
 
 PRODUCT_OTA_PUBLIC_KEYS := $(sort $(PRODUCT_OTA_PUBLIC_KEYS))
 PRODUCT_EXTRA_RECOVERY_KEYS := $(sort $(PRODUCT_EXTRA_RECOVERY_KEYS))
diff --git a/core/sdk_check.mk b/core/sdk_check.mk
index c09fc7c..09fd0eb 100644
--- a/core/sdk_check.mk
+++ b/core/sdk_check.mk
@@ -6,7 +6,7 @@
 # be set to a particular module class to enable warnings and errors for that
 # subtype.
 
-whitelisted_modules := framework-res__auto_generated_rro
+allowed_modules := framework-res__auto_generated_rro
 
 
 ifeq (,$(JAVA_SDK_ENFORCEMENT_ERROR))
@@ -14,7 +14,7 @@
 endif
 
 ifeq ($(LOCAL_SDK_VERSION)$(LOCAL_PRIVATE_PLATFORM_APIS),)
-  ifeq (,$(filter $(LOCAL_MODULE),$(whitelisted_modules)))
+  ifeq (,$(filter $(LOCAL_MODULE),$(allowed_modules)))
     ifneq ($(JAVA_SDK_ENFORCEMENT_WARNING)$(JAVA_SDK_ENFORCEMENT_ERROR),)
       my_message := Must specify LOCAL_SDK_VERSION or LOCAL_PRIVATE_PLATFORM_APIS,
       ifeq ($(LOCAL_MODULE_CLASS),$(JAVA_SDK_ENFORCEMENT_ERROR))
diff --git a/core/soong_android_app_set.mk b/core/soong_android_app_set.mk
index 5ed9b2c..c884894 100644
--- a/core/soong_android_app_set.mk
+++ b/core/soong_android_app_set.mk
@@ -31,4 +31,9 @@
 $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
 PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
 
+PACKAGES := $(PACKAGES) $(LOCAL_MODULE)
+# We can't know exactly what apk files would be outputted yet.
+# Let extract_apks generate apkcerts.txt and merge it later.
+PACKAGES.$(LOCAL_MODULE).APKCERTS_FILE := $(LOCAL_APKCERTS_FILE)
+
 SOONG_ALREADY_CONV += $(LOCAL_MODULE)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 18949be..b9e7a27 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -39,7 +39,8 @@
 $(call add_json_str,  Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))
 
 $(call add_json_bool, Allow_missing_dependencies,        $(ALLOW_MISSING_DEPENDENCIES))
-$(call add_json_bool, Unbundled_build,                   $(TARGET_BUILD_APPS))
+$(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_sdks_from_source,  $(UNBUNDLED_BUILD_SDKS_FROM_SOURCE))
 $(call add_json_bool, Pdk,                               $(filter true,$(TARGET_BUILD_PDK)))
 
@@ -110,11 +111,17 @@
 $(call add_json_bool, ClangTidy,                         $(filter 1 true,$(WITH_TIDY)))
 $(call add_json_str,  TidyChecks,                        $(WITH_TIDY_CHECKS))
 
-$(call add_json_bool, NativeLineCoverage,                $(filter true,$(NATIVE_LINE_COVERAGE)))
-$(call add_json_bool, Native_coverage,                   $(filter true,$(NATIVE_COVERAGE)))
+$(call add_json_list, JavaCoveragePaths,                 $(JAVA_COVERAGE_PATHS))
+$(call add_json_list, JavaCoverageExcludePaths,          $(JAVA_COVERAGE_EXCLUDE_PATHS))
+
+$(call add_json_bool, GcovCoverage,                      $(filter true,$(NATIVE_COVERAGE)))
 $(call add_json_bool, ClangCoverage,                     $(filter true,$(CLANG_COVERAGE)))
-$(call add_json_list, CoveragePaths,                     $(COVERAGE_PATHS))
-$(call add_json_list, CoverageExcludePaths,              $(COVERAGE_EXCLUDE_PATHS))
+# TODO(b/158212027): Remove `$(COVERAGE_PATHS)` from this list when all users have been moved to
+# `NATIVE_COVERAGE_PATHS`.
+$(call add_json_list, NativeCoveragePaths,               $(COVERAGE_PATHS) $(NATIVE_COVERAGE_PATHS))
+# TODO(b/158212027): Remove `$(COVERAGE_EXCLUDE_PATHS)` from this list when all users have been
+# moved to `NATIVE_COVERAGE_EXCLUDE_PATHS`.
+$(call add_json_list, NativeCoverageExcludePaths,        $(COVERAGE_EXCLUDE_PATHS) $(NATIVE_COVERAGE_EXCLUDE_PATHS))
 
 $(call add_json_bool, SamplingPGO,                       $(filter true,$(SAMPLING_PGO)))
 
@@ -122,8 +129,6 @@
 $(call add_json_bool, Binder32bit,                       $(BINDER32BIT))
 $(call add_json_str,  BtConfigIncludeDir,                $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))
 $(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
-$(call add_json_bool, DevicePrefer32BitApps,             $(filter true,$(TARGET_PREFER_32_BIT_APPS)))
-$(call add_json_bool, DevicePrefer32BitExecutables,      $(filter true,$(TARGET_PREFER_32_BIT_EXECUTABLES)))
 $(call add_json_str,  DeviceVndkVersion,                 $(BOARD_VNDK_VERSION))
 $(call add_json_str,  Platform_vndk_version,             $(PLATFORM_VNDK_VERSION))
 $(call add_json_str,  ProductVndkVersion,                $(PRODUCT_PRODUCT_VNDK_VERSION))
@@ -184,7 +189,7 @@
 $(call add_json_list, CertificateOverrides,              $(PRODUCT_CERTIFICATE_OVERRIDES))
 
 $(call add_json_bool, EnforceSystemCertificate,          $(ENFORCE_SYSTEM_CERTIFICATE))
-$(call add_json_list, EnforceSystemCertificateWhitelist, $(ENFORCE_SYSTEM_CERTIFICATE_WHITELIST))
+$(call add_json_list, EnforceSystemCertificateAllowList, $(ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST))
 
 $(call add_json_list, ProductHiddenAPIStubs,             $(PRODUCT_HIDDENAPI_STUBS))
 $(call add_json_list, ProductHiddenAPIStubsSystem,       $(PRODUCT_HIDDENAPI_STUBS_SYSTEM))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 14fa828..f1311ed 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -71,10 +71,22 @@
 define build-properties
 ALL_DEFAULT_INSTALLED_MODULES += $(2)
 
-# TODO(b/117892318): eliminate the call to uniq-pairs-by-first-component when
-# it is guaranteed that there is no dup.
+$(eval # Properties can be assigned using `prop ?= value` or `prop = value` syntax.)
+$(eval # Eliminate spaces around the ?= and = separators.)
 $(foreach name,$(strip $(4)),\
-    $(eval _resolved_$(name) := $$(call collapse-pairs, $$(call uniq-pairs-by-first-component,$$($(name)),=)))\
+    $(eval _temp := $$(call collapse-pairs,$$($(name)),?=))\
+    $(eval _resolved_$(name) := $$(call collapse-pairs,$$(_temp),=))\
+)
+
+$(eval # Implement the legacy behavior when BUILD_BROKEN_DUP_SYSPROP is on.)
+$(eval # Optional assignments are all converted to normal assignments and)
+$(eval # when their duplicates the first one wins)
+$(if $(filter true,$(BUILD_BROKEN_DUP_SYSPROP)),\
+    $(foreach name,$(strip $(4)),\
+        $(eval _temp := $$(subst ?=,=,$$(_resolved_$(name))))\
+        $(eval _resolved_$(name) := $$(call uniq-pairs-by-first-component,$$(_resolved_$(name)),=))\
+    )\
+    $(eval _option := --allow-dup)\
 )
 
 $(2): $(POST_PROCESS_PROPS) $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) $(3)
@@ -99,7 +111,7 @@
 	        echo "$$(line)" >> $$@;\
 	    )\
 	)
-	$(hide) $(POST_PROCESS_PROPS) $$@ $(5)
+	$(hide) $(POST_PROCESS_PROPS) $$(_option) $$@ $(5)
 	$(hide) echo "# end of file" >> $$@
 endef
 
@@ -280,6 +292,10 @@
 # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
 _prop_vars_ := \
     ADDITIONAL_SYSTEM_PROPERTIES \
+    PRODUCT_SYSTEM_PROPERTIES
+
+# TODO(b/117892318): deprecate this
+_prop_vars_ += \
     PRODUCT_SYSTEM_DEFAULT_PROPERTIES
 
 ifndef property_overrides_split_enabled
@@ -316,6 +332,11 @@
 # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
 _prop_vars_ := \
     ADDITIONAL_VENDOR_PROPERTIES \
+    PRODUCT_VENDOR_PROPERTIES
+
+# TODO(b/117892318): deprecate this
+_prop_vars_ += \
+    PRODUCT_DEFAULT_PROPERTY_OVERRIDES \
     PRODUCT_PROPERTY_OVERRIDES
 else
 _prop_vars_ :=
@@ -352,7 +373,7 @@
     $(empty)))
 
 # ----------------------------------------------------------------
-# odm/build.prop
+# odm/etc/build.prop
 #
 _prop_files_ := $(if $(TARGET_ODM_PROP),\
     $(TARGET_ODM_PROP),\
@@ -364,7 +385,9 @@
     ADDITIONAL_ODM_PROPERTIES \
     PRODUCT_ODM_PROPERTIES
 
-INSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/build.prop
+# Note the 'etc' sub directory. For the reason, see
+# I0733c277baa67c549bb45599abb70aba13fbdbcf
+INSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/etc/build.prop
 $(eval $(call build-properties,\
     odm,\
     $(INSTALLED_ODM_BUILD_PROP_TARGET),\
diff --git a/core/tasks/boot_jars_package_check.mk b/core/tasks/boot_jars_package_check.mk
index 02d7212..a17aaff 100644
--- a/core/tasks/boot_jars_package_check.mk
+++ b/core/tasks/boot_jars_package_check.mk
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 #
-# Rules to check if classes in the boot jars are from the whitelisted packages.
+# Rules to check if classes in the boot jars are from the list of allowed packages.
 #
 
 ifneq ($(SKIP_BOOT_JARS_CHECK),true)
@@ -44,14 +44,14 @@
   $(call intermediates-dir-for, JAVA_LIBRARIES, $(j),,COMMON)/classes.jar)
 
 script := build/make/core/tasks/check_boot_jars/check_boot_jars.py
-whitelist_file := build/make/core/tasks/check_boot_jars/package_whitelist.txt
+allowed_file := build/make/core/tasks/check_boot_jars/package_allowed_list.txt
 
 $(stamp): PRIVATE_BOOT_JARS := $(built_boot_jars)
 $(stamp): PRIVATE_SCRIPT := $(script)
-$(stamp): PRIVATE_WHITELIST := $(whitelist_file)
-$(stamp) : $(built_boot_jars) $(script) $(whitelist_file)
+$(stamp): PRIVATE_ALLOWED := $(allowed_file)
+$(stamp) : $(built_boot_jars) $(script) $(allowed_file)
 	@echo "Check package name for $(PRIVATE_BOOT_JARS)"
-	$(hide) $(PRIVATE_SCRIPT) $(PRIVATE_WHITELIST) $(PRIVATE_BOOT_JARS)
+	$(hide) $(PRIVATE_SCRIPT) $(PRIVATE_ALLOWED) $(PRIVATE_BOOT_JARS)
 	$(hide) mkdir -p $(dir $@) && touch $@
 
 .PHONY: check-boot-jars
diff --git a/core/tasks/check_boot_jars/check_boot_jars.py b/core/tasks/check_boot_jars/check_boot_jars.py
index 67b73d5..cf4ef27 100755
--- a/core/tasks/check_boot_jars/check_boot_jars.py
+++ b/core/tasks/check_boot_jars/check_boot_jars.py
@@ -3,7 +3,7 @@
 """
 Check boot jars.
 
-Usage: check_boot_jars.py <package_whitelist_file> <jar1> <jar2> ...
+Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
 """
 import logging
 import os.path
@@ -12,12 +12,12 @@
 import sys
 
 
-# The compiled whitelist RE.
-whitelist_re = None
+# The compiled allow list RE.
+allow_list_re = None
 
 
-def LoadWhitelist(filename):
-  """ Load and compile whitelist regular expressions from filename.
+def LoadAllowList(filename):
+  """ Load and compile allow list regular expressions from filename.
   """
   lines = []
   with open(filename, 'r') as f:
@@ -27,19 +27,19 @@
         continue
       lines.append(line)
   combined_re = r'^(%s)$' % '|'.join(lines)
-  global whitelist_re
+  global allow_list_re
   try:
-    whitelist_re = re.compile(combined_re)
+    allow_list_re = re.compile(combined_re)
   except re.error:
     logging.exception(
-        'Cannot compile package whitelist regular expression: %r',
+        'Cannot compile package allow list regular expression: %r',
         combined_re)
-    whitelist_re = None
+    allow_list_re = None
     return False
   return True
 
 
-def CheckJar(whitelist_path, jar):
+def CheckJar(allow_list_path, jar):
   """Check a jar file.
   """
   # Get the list of files inside the jar file.
@@ -49,15 +49,20 @@
   if p.returncode != 0:
     return False
   items = stdout.split()
+  classes = 0
   for f in items:
     if f.endswith('.class'):
+      classes += 1
       package_name = os.path.dirname(f)
       package_name = package_name.replace('/', '.')
-      if not package_name or not whitelist_re.match(package_name):
+      if not package_name or not allow_list_re.match(package_name):
         print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
-                              ' not in the whitelist %s of packages allowed on the bootclasspath.'
-                              % (jar, f, package_name, whitelist_path))
+                              ' not in the allow list %s of packages allowed on the bootclasspath.'
+                              % (jar, f, package_name, allow_list_path))
         return False
+  if classes == 0:
+    print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
+    return False
   return True
 
 
@@ -65,14 +70,14 @@
   if len(argv) < 2:
     print __doc__
     return 1
-  whitelist_path = argv[0]
+  allow_list_path = argv[0]
 
-  if not LoadWhitelist(whitelist_path):
+  if not LoadAllowList(allow_list_path):
     return 1
 
   passed = True
   for jar in argv[1:]:
-    if not CheckJar(whitelist_path, jar):
+    if not CheckJar(allow_list_path, jar):
       passed = False
   if not passed:
     return 1
diff --git a/core/tasks/check_boot_jars/package_whitelist.txt b/core/tasks/check_boot_jars/package_allowed_list.txt
similarity index 99%
rename from core/tasks/check_boot_jars/package_whitelist.txt
rename to core/tasks/check_boot_jars/package_allowed_list.txt
index d4f600a..6240ffd 100644
--- a/core/tasks/check_boot_jars/package_whitelist.txt
+++ b/core/tasks/check_boot_jars/package_allowed_list.txt
@@ -1,4 +1,4 @@
-# Boot jar package name whitelist.
+# Boot jar package name allowed list.
 # Each line is interpreted as a regular expression.
 
 ###################################################
diff --git a/core/tasks/general-tests.mk b/core/tasks/general-tests.mk
index cb84a5c..a820a28 100644
--- a/core/tasks/general-tests.mk
+++ b/core/tasks/general-tests.mk
@@ -14,12 +14,10 @@
 
 .PHONY: general-tests
 
-# TODO(b/149249068): Remove vts10-tradefed.jar after all VTS tests are converted
 general_tests_tools := \
     $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar \
     $(HOST_OUT_JAVA_LIBRARIES)/compatibility-host-util.jar \
     $(HOST_OUT_JAVA_LIBRARIES)/vts-tradefed.jar \
-    $(HOST_OUT_JAVA_LIBRARIES)/vts10-tradefed.jar
 
 intermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests)
 general_tests_zip := $(PRODUCT_OUT)/general-tests.zip
diff --git a/core/tasks/vendor_module_check.mk b/core/tasks/vendor_module_check.mk
index 0b8f1e8..b4c5a3b 100644
--- a/core/tasks/vendor_module_check.mk
+++ b/core/tasks/vendor_module_check.mk
@@ -15,7 +15,7 @@
 #
 
 # Restrict the vendor module owners here.
-_vendor_owner_whitelist := \
+_vendor_owner_allowed_list := \
         asus \
         audience \
         atmel \
@@ -87,14 +87,14 @@
     $(filter vendor/%, $(PRODUCT_COPY_FILES)))
 ifneq (,$(_vendor_check_copy_files))
 $(foreach c, $(_vendor_check_copy_files), \
-  $(if $(filter $(_vendor_owner_whitelist), $(call word-colon,3,$(c))),,\
+  $(if $(filter $(_vendor_owner_allowed_list), $(call word-colon,3,$(c))),,\
     $(error Error: vendor PRODUCT_COPY_FILES file "$(c)" has unknown owner))\
   $(eval _vendor_module_owner_info += $(call word-colon,2,$(c)):$(call word-colon,3,$(c))))
 endif
 _vendor_check_copy_files :=
 
 $(foreach m, $(_vendor_check_modules), \
-  $(if $(filter $(_vendor_owner_whitelist), $(ALL_MODULES.$(m).OWNER)),,\
+  $(if $(filter $(_vendor_owner_allowed_list), $(ALL_MODULES.$(m).OWNER)),,\
     $(error Error: vendor module "$(m)" in $(ALL_MODULES.$(m).PATH) with unknown owner \
       "$(ALL_MODULES.$(m).OWNER)" in product "$(TARGET_PRODUCT)"))\
   $(if $(ALL_MODULES.$(m).INSTALLED),\
diff --git a/rbesetup.sh b/rbesetup.sh
index 7e9b2ea..145e1e8 100644
--- a/rbesetup.sh
+++ b/rbesetup.sh
@@ -4,7 +4,7 @@
 # for the build to be executed with RBE.
 function use_rbe() {
   local RBE_LOG_DIR="/tmp"
-  local RBE_BINARIES_DIR="prebuilts/remoteexecution-client/latest/"
+  local RBE_BINARIES_DIR="prebuilts/remoteexecution-client/latest"
   local DOCKER_IMAGE="gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62"
 
   # Do not set an invocation-ID and let reproxy auto-generate one.
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index 49f6edc..0f36aae 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -58,7 +58,7 @@
 BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
 BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216
 
-# Setup a vendor image to let PRODUCT_PROPERTY_OVERRIDES does not affect GSI
+# Setup a vendor image to let PRODUCT_VENDOR_PROPERTIES does not affect GSI
 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
 
 # Disable 64 bit mediadrmserver
diff --git a/target/board/emulator_arm64/device.mk b/target/board/emulator_arm64/device.mk
index 57675d0..73dc2f4 100644
--- a/target/board/emulator_arm64/device.mk
+++ b/target/board/emulator_arm64/device.mk
@@ -26,7 +26,3 @@
 
 PRODUCT_COPY_FILES += \
     $(LOCAL_KERNEL):kernel
-
-# Adjust the Dalvik heap to be appropriate for a tablet.
-$(call inherit-product-if-exists, frameworks/base/build/tablet-dalvik-heap.mk)
-$(call inherit-product-if-exists, frameworks/native/build/tablet-dalvik-heap.mk)
diff --git a/target/board/emulator_x86/device.mk b/target/board/emulator_x86/device.mk
index 7da09a9..8a9d8da 100644
--- a/target/board/emulator_x86/device.mk
+++ b/target/board/emulator_x86/device.mk
@@ -18,7 +18,7 @@
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
 
 ifdef NET_ETH0_STARTONBOOT
-  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
+  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
 # Ensure we package the BIOS files too.
diff --git a/target/board/emulator_x86_64/device.mk b/target/board/emulator_x86_64/device.mk
index 7da09a9..8a9d8da 100755
--- a/target/board/emulator_x86_64/device.mk
+++ b/target/board/emulator_x86_64/device.mk
@@ -18,7 +18,7 @@
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
 
 ifdef NET_ETH0_STARTONBOOT
-  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
+  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
 # Ensure we package the BIOS files too.
diff --git a/target/board/generic/device.mk b/target/board/generic/device.mk
index cfb15f0..76242c9 100644
--- a/target/board/generic/device.mk
+++ b/target/board/generic/device.mk
@@ -16,12 +16,3 @@
 
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for libwifi-hal-emu
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
-
-# NFC:
-#   Provide default libnfc-nci.conf file for devices that does not have one in
-#   vendor/etc because aosp system image (of aosp_$arch products) is going to
-#   be used as GSI.
-#   May need to remove the following for newly launched devices in P since this
-#   NFC configuration file should be in vendor/etc, instead of system/etc
-PRODUCT_COPY_FILES += \
-    device/generic/common/nfc/libnfc-nci.conf:system/etc/libnfc-nci.conf
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index 3b7cd44..b34004f 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -18,7 +18,3 @@
     device/google/cuttlefish_kernel/5.4-arm64/kernel-5.4:kernel-5.4 \
     device/google/cuttlefish_kernel/5.4-arm64/kernel-5.4-gz:kernel-5.4-gz \
     device/google/cuttlefish_kernel/5.4-arm64/kernel-5.4-lz4:kernel-5.4-lz4
-
-# Adjust the Dalvik heap to be appropriate for a tablet.
-$(call inherit-product-if-exists, frameworks/base/build/tablet-dalvik-heap.mk)
-$(call inherit-product-if-exists, frameworks/native/build/tablet-dalvik-heap.mk)
diff --git a/target/board/generic_x86/device.mk b/target/board/generic_x86/device.mk
index 2b10a3d..5ad008f 100644
--- a/target/board/generic_x86/device.mk
+++ b/target/board/generic_x86/device.mk
@@ -18,7 +18,7 @@
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
 
 ifdef NET_ETH0_STARTONBOOT
-  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
+  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
 # Ensure we package the BIOS files too.
diff --git a/target/board/generic_x86_64/device.mk b/target/board/generic_x86_64/device.mk
index 2b10a3d..5ad008f 100755
--- a/target/board/generic_x86_64/device.mk
+++ b/target/board/generic_x86_64/device.mk
@@ -18,7 +18,7 @@
 PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for goldfish deps.
 
 ifdef NET_ETH0_STARTONBOOT
-  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
+  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
 # Ensure we package the BIOS files too.
diff --git a/target/product/aosp_arm.mk b/target/product/aosp_arm.mk
index 0607717..0cec14b 100644
--- a/target/product/aosp_arm.mk
+++ b/target/product/aosp_arm.mk
@@ -33,7 +33,7 @@
 PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
 endif
 
-PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
 
 #
 # All components inherited here go to system_ext image
diff --git a/target/product/aosp_arm64_ab.mk b/target/product/aosp_arm64_ab.mk
index 75b9cc4..0b2ae09 100644
--- a/target/product/aosp_arm64_ab.mk
+++ b/target/product/aosp_arm64_ab.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# PRODUCT_PROPERTY_OVERRIDES cannot be used here because sysprops will be at
+# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
 # /vendor/[build|default].prop when build split is on. In order to have sysprops
 # on the generic system image, place them in build/make/target/board/
 # gsi_system.prop.
diff --git a/target/product/aosp_arm_ab.mk b/target/product/aosp_arm_ab.mk
index 80ebdb1..ec2e5d7 100644
--- a/target/product/aosp_arm_ab.mk
+++ b/target/product/aosp_arm_ab.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# PRODUCT_PROPERTY_OVERRIDES cannot be used here because sysprops will be at
+# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
 # /vendor/[build|default].prop when build split is on. In order to have sysprops
 # on the generic system image, place them in build/make/target/board/
 # gsi_system.prop.
diff --git a/target/product/aosp_product.mk b/target/product/aosp_product.mk
index 9b9ccb1..a3da1c9 100644
--- a/target/product/aosp_product.mk
+++ b/target/product/aosp_product.mk
@@ -23,9 +23,9 @@
 
 # Additional settings used in all AOSP builds
 PRODUCT_PRODUCT_PROPERTIES += \
-    ro.config.ringtone=Ring_Synth_04.ogg \
-    ro.config.notification_sound=pixiedust.ogg \
-    ro.com.android.dataroaming=true \
+    ro.config.ringtone?=Ring_Synth_04.ogg \
+    ro.config.notification_sound?=pixiedust.ogg \
+    ro.com.android.dataroaming?=true \
 
 # More AOSP packages
 PRODUCT_PACKAGES += \
@@ -37,8 +37,3 @@
 #   Provide a APN configuration to GSI product
 PRODUCT_COPY_FILES += \
     device/sample/etc/apns-full-conf.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/apns-conf.xml
-
-# NFC:
-#   Provide a libnfc-nci.conf to GSI product
-PRODUCT_COPY_FILES += \
-    device/generic/common/nfc/libnfc-nci.conf:$(TARGET_COPY_OUT_PRODUCT)/etc/libnfc-nci.conf
diff --git a/target/product/aosp_x86_64_ab.mk b/target/product/aosp_x86_64_ab.mk
index 9d23fc7..578a254 100644
--- a/target/product/aosp_x86_64_ab.mk
+++ b/target/product/aosp_x86_64_ab.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# PRODUCT_PROPERTY_OVERRIDES cannot be used here because sysprops will be at
+# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
 # /vendor/[build|default].prop when build split is on. In order to have sysprops
 # on the generic system image, place them in build/make/target/board/
 # gsi_system.prop.
diff --git a/target/product/aosp_x86_ab.mk b/target/product/aosp_x86_ab.mk
index 6b6a4c6..40c1d83 100644
--- a/target/product/aosp_x86_ab.mk
+++ b/target/product/aosp_x86_ab.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# PRODUCT_PROPERTY_OVERRIDES cannot be used here because sysprops will be at
+# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
 # /vendor/[build|default].prop when build split is on. In order to have sysprops
 # on the generic system image, place them in build/make/target/board/
 # gsi_system.prop.
diff --git a/target/product/aosp_x86_arm.mk b/target/product/aosp_x86_arm.mk
index 7b9b89c..deba3d9 100644
--- a/target/product/aosp_x86_arm.mk
+++ b/target/product/aosp_x86_arm.mk
@@ -27,7 +27,7 @@
 endif
 
 # TODO (b/138382074): remove following setting after enable product/system_ext
-PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
     system/product/% \
     system/system_ext/%
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 9e82048..f6770fb 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -348,9 +348,9 @@
 endif
 
 PRODUCT_COPY_FILES += system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.zygote=zygote32
+PRODUCT_SYSTEM_PROPERTIES += ro.zygote?=zygote32
 
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += debug.atrace.tags.enableflags=0
+PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0
 
 # Packages included only for eng or userdebug builds, previously debug tagged
 PRODUCT_PACKAGES_DEBUG := \
diff --git a/target/product/core_64_bit.mk b/target/product/core_64_bit.mk
index f9baa27..7fa6ed2 100644
--- a/target/product/core_64_bit.mk
+++ b/target/product/core_64_bit.mk
@@ -27,7 +27,7 @@
 
 # Set the zygote property to select the 64-bit primary, 32-bit secondary script
 # This line must be parsed before the one in core_minimal.mk
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.zygote=zygote64_32
+PRODUCT_SYSTEM_PROPERTIES += ro.zygote=zygote64_32
 
 TARGET_SUPPORTS_32_BIT_APPS := true
 TARGET_SUPPORTS_64_BIT_APPS := true
diff --git a/target/product/core_64_bit_only.mk b/target/product/core_64_bit_only.mk
index 8901a50..63beea9 100644
--- a/target/product/core_64_bit_only.mk
+++ b/target/product/core_64_bit_only.mk
@@ -24,7 +24,7 @@
 
 # Set the zygote property to select the 64-bit script.
 # This line must be parsed before the one in core_minimal.mk
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.zygote=zygote64
+PRODUCT_SYSTEM_PROPERTIES += ro.zygote=zygote64
 
 TARGET_SUPPORTS_32_BIT_APPS := false
 TARGET_SUPPORTS_64_BIT_APPS := true
diff --git a/target/product/emulated_storage.mk b/target/product/emulated_storage.mk
index d7cc9ce..4c6c644 100644
--- a/target/product/emulated_storage.mk
+++ b/target/product/emulated_storage.mk
@@ -15,7 +15,7 @@
 #
 
 PRODUCT_QUOTA_PROJID := 1
-PRODUCT_PROPERTY_OVERRIDES += external_storage.projid.enabled=1
+PRODUCT_VENDOR_PROPERTIES += external_storage.projid.enabled=1
 
 PRODUCT_FS_CASEFOLD := 1
-PRODUCT_PROPERTY_OVERRIDES += external_storage.casefold.enabled=1
+PRODUCT_VENDOR_PROPERTIES += external_storage.casefold.enabled=1
diff --git a/target/product/emulator.mk b/target/product/emulator.mk
index 52fa1a7..9dffc1a 100644
--- a/target/product/emulator.mk
+++ b/target/product/emulator.mk
@@ -47,7 +47,7 @@
 #responding, disble it for now.
 #still keep it on internal master as it is still working
 #once it is fixed in aosp, remove this block of comment.
-#PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+#PRODUCT_VENDOR_PROPERTIES += \
 #config.disable_location=true
 
 # Enable Perfetto traced
diff --git a/target/product/emulator_system.mk b/target/product/emulator_system.mk
index 4b6987c..b7e7cfa 100644
--- a/target/product/emulator_system.mk
+++ b/target/product/emulator_system.mk
@@ -16,7 +16,7 @@
 # This file lists emulator experimental modules added to PRODUCT_PACKAGES,
 # only included by targets sdk_phone_x86/64 and sdk_gphone_x86/64
 
-PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST := \
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
     system/lib/libemulator_multidisplay_jni.so \
     system/lib64/libemulator_multidisplay_jni.so \
     system/priv-app/MultiDisplayProvider/MultiDisplayProvider.apk \
diff --git a/target/product/emulator_vendor.mk b/target/product/emulator_vendor.mk
index 7891a26..bb679ec 100644
--- a/target/product/emulator_vendor.mk
+++ b/target/product/emulator_vendor.mk
@@ -39,7 +39,7 @@
 #responding, disble it for now.
 #still keep it on internal master as it is still working
 #once it is fixed in aosp, remove this block of comment.
-#PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+#PRODUCT_VENDOR_PROPERTIES += \
 #config.disable_location=true
 
 # Enable Perfetto traced
diff --git a/target/product/full_base.mk b/target/product/full_base.mk
index 447576c..64f61ff 100644
--- a/target/product/full_base.mk
+++ b/target/product/full_base.mk
@@ -42,9 +42,9 @@
     netutils-wrapper-1.0
 
 # Additional settings used in all AOSP builds
-PRODUCT_PROPERTY_OVERRIDES := \
-    ro.config.ringtone=Ring_Synth_04.ogg \
-    ro.config.notification_sound=pixiedust.ogg
+PRODUCT_VENDOR_PROPERTIES := \
+    ro.config.ringtone?=Ring_Synth_04.ogg \
+    ro.config.notification_sound?=pixiedust.ogg
 
 # Put en_US first in the list, so make it default.
 PRODUCT_LOCALES := en_US
diff --git a/target/product/full_base_telephony.mk b/target/product/full_base_telephony.mk
index af4097d..d8a54cd 100644
--- a/target/product/full_base_telephony.mk
+++ b/target/product/full_base_telephony.mk
@@ -19,9 +19,9 @@
 # build quite specifically for the emulator, and might not be
 # entirely appropriate to inherit from for on-device configurations.
 
-PRODUCT_PROPERTY_OVERRIDES := \
-    keyguard.no_require_sim=true \
-    ro.com.android.dataroaming=true
+PRODUCT_VENDOR_PROPERTIES := \
+    keyguard.no_require_sim?=true \
+    ro.com.android.dataroaming?=true
 
 PRODUCT_COPY_FILES := \
     device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml \
diff --git a/target/product/full_x86.mk b/target/product/full_x86.mk
index 55c450a..2f40c03 100644
--- a/target/product/full_x86.mk
+++ b/target/product/full_x86.mk
@@ -28,7 +28,7 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
 
 ifdef NET_ETH0_STARTONBOOT
-  PRODUCT_PROPERTY_OVERRIDES += net.eth0.startonboot=1
+  PRODUCT_VENDOR_PROPERTIES += net.eth0.startonboot=1
 endif
 
 # Ensure we package the BIOS files too.
diff --git a/target/product/generic.mk b/target/product/generic.mk
index a1acaab..d3f81b1 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -28,5 +28,5 @@
 PRODUCT_DEVICE := generic
 PRODUCT_NAME := generic
 
-whitelist := product_manifest.xml
-$(call enforce-product-packages-exist,$(whitelist))
+allowed_list := product_manifest.xml
+$(call enforce-product-packages-exist,$(allowed_list))
diff --git a/target/product/go_defaults_common.mk b/target/product/go_defaults_common.mk
index 3e42b68..d324aa9 100644
--- a/target/product/go_defaults_common.mk
+++ b/target/product/go_defaults_common.mk
@@ -18,7 +18,7 @@
 
 
 # Set lowram options and enable traced by default
-PRODUCT_PROPERTY_OVERRIDES += \
+PRODUCT_VENDOR_PROPERTIES += \
      ro.config.low_ram=true \
      persist.traced.enable=1 \
 
diff --git a/target/product/gsi/gsi_skip_mount.cfg b/target/product/gsi/gsi_skip_mount.cfg
index 3f812cb..ad3c7d9 100644
--- a/target/product/gsi/gsi_skip_mount.cfg
+++ b/target/product/gsi/gsi_skip_mount.cfg
@@ -1,2 +1,3 @@
+/oem
 /product
 /system_ext
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index b70ba3d..a61585e 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -25,7 +25,7 @@
 #
 
 # Exclude all files under system/product and system/system_ext
-PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
     system/product/% \
     system/system_ext/%
 
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index b540fdf..e2c91b6 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -83,7 +83,7 @@
 PRODUCT_COPY_FILES += \
     frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf
 
-PRODUCT_PROPERTY_OVERRIDES += \
-    ro.carrier=unknown \
-    ro.config.notification_sound=OnTheHunt.ogg \
-    ro.config.alarm_alert=Alarm_Classic.ogg
+PRODUCT_VENDOR_PROPERTIES += \
+    ro.carrier?=unknown \
+    ro.config.notification_sound?=OnTheHunt.ogg \
+    ro.config.alarm_alert?=Alarm_Classic.ogg
diff --git a/target/product/legacy_gsi_release.mk b/target/product/legacy_gsi_release.mk
index f072481..c1646bb 100644
--- a/target/product/legacy_gsi_release.mk
+++ b/target/product/legacy_gsi_release.mk
@@ -16,7 +16,7 @@
 
 include $(SRC_TARGET_DIR)/product/gsi_release.mk
 
-PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
     system/etc/init/init.legacy-gsi.rc \
     system/etc/init/gsi/init.vndk-27.rc \
     system/etc/ld.config.vndk_lite.txt \
diff --git a/target/product/mainline_system.mk b/target/product/mainline_system.mk
index bed1b28..e3b3e7d 100644
--- a/target/product/mainline_system.mk
+++ b/target/product/mainline_system.mk
@@ -118,7 +118,6 @@
 PRODUCT_COPY_FILES += \
     system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc \
     system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \
-    system/core/rootdir/init.zygote32_64.rc:system/etc/init/hw/init.zygote32_64.rc \
     system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc \
 
 # Enable dynamic partition size
@@ -140,13 +139,13 @@
 PRODUCT_SYSTEM_MODEL := mainline
 PRODUCT_SYSTEM_DEVICE := generic
 
-_base_mk_whitelist :=
+_base_mk_allowed_list :=
 
-_my_whitelist := $(_base_mk_whitelist)
+_my_allowed_list := $(_base_mk_allowed_list)
 
 # For mainline, system.img should be mounted at /, so we include ROOT here.
 _my_paths := \
   $(TARGET_COPY_OUT_ROOT)/ \
   $(TARGET_COPY_OUT_SYSTEM)/ \
 
-$(call require-artifacts-in-path, $(_my_paths), $(_my_whitelist))
+$(call require-artifacts-in-path, $(_my_paths), $(_my_allowed_list))
diff --git a/target/product/media_system.mk b/target/product/media_system.mk
index a83e609..7a2dd73 100644
--- a/target/product/media_system.mk
+++ b/target/product/media_system.mk
@@ -73,11 +73,11 @@
 
 # On userdebug builds, collect more tombstones by default.
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
-    tombstoned.max_tombstone_count=50
+PRODUCT_VENDOR_PROPERTIES += \
+    tombstoned.max_tombstone_count?=50
 endif
 
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+PRODUCT_VENDOR_PROPERTIES += \
     ro.logd.size.stats=64K \
     log.tag.stats_log=I
 
diff --git a/target/product/profile_boot_common.mk b/target/product/profile_boot_common.mk
index a40b3e9..fa2e163 100644
--- a/target/product/profile_boot_common.mk
+++ b/target/product/profile_boot_common.mk
@@ -23,7 +23,7 @@
 DEX_PREOPT_DEFAULT := nostripping
 
 # Boot image property overrides.
-PRODUCT_PROPERTY_OVERRIDES += \
+PRODUCT_VENDOR_PROPERTIES += \
     dalvik.vm.profilesystemserver=true \
     dalvik.vm.profilebootclasspath=true
 
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index 84b1252..b96601d 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -40,7 +40,7 @@
 PRODUCT_PACKAGES += \
     hiddenapi-package-whitelist.xml \
 
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.image-dex2oat-Xms=64m \
     dalvik.vm.image-dex2oat-Xmx=64m \
     dalvik.vm.dex2oat-Xms=64m \
@@ -50,50 +50,50 @@
     dalvik.vm.dexopt.secondary=true \
     dalvik.vm.appimageformat=lz4
 
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     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.
 ifeq (eng,$(TARGET_BUILD_VARIANT))
-    PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
-        pm.dexopt.first-boot=extract \
-        pm.dexopt.boot=extract
+    PRODUCT_SYSTEM_PROPERTIES += \
+        pm.dexopt.first-boot?=extract \
+        pm.dexopt.boot?=extract
 else
-    PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
-        pm.dexopt.first-boot=quicken \
-        pm.dexopt.boot=verify
+    PRODUCT_SYSTEM_PROPERTIES += \
+        pm.dexopt.first-boot?=quicken \
+        pm.dexopt.boot?=verify
 endif
 
 # The install filter is speed-profile in order to enable the use of
 # profiles from the dex metadata files. Note that if a profile is not provided
 # or if it is empty speed-profile is equivalent to (quicken + empty app image).
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
-    pm.dexopt.install=speed-profile \
-    pm.dexopt.bg-dexopt=speed-profile \
-    pm.dexopt.ab-ota=speed-profile \
-    pm.dexopt.inactive=verify \
-    pm.dexopt.shared=speed
+PRODUCT_SYSTEM_PROPERTIES += \
+    pm.dexopt.install?=speed-profile \
+    pm.dexopt.bg-dexopt?=speed-profile \
+    pm.dexopt.ab-ota?=speed-profile \
+    pm.dexopt.inactive?=verify \
+    pm.dexopt.shared?=speed
 
 # Pass file with the list of updatable boot class path packages to dex2oat.
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.dex2oat-updatable-bcp-packages-file=/system/etc/updatable-bcp-packages.txt
 
 # Enable resolution of startup const strings.
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.dex2oat-resolve-startup-strings=true
 
 # Specify default block size of 512K to enable parallel image decompression.
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.dex2oat-max-image-block-size=524288
 
 # Enable minidebuginfo generation unless overridden.
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     dalvik.vm.minidebuginfo=true \
     dalvik.vm.dex2oat-minidebuginfo=true
 
 # Disable iorapd by default
-PRODUCT_SYSTEM_DEFAULT_PROPERTIES += \
+PRODUCT_SYSTEM_PROPERTIES += \
     ro.iorapd.enable=false
 
 PRODUCT_USES_DEFAULT_ART_CONFIG := true
diff --git a/target/product/updatable_apex.mk b/target/product/updatable_apex.mk
index e5a647c..2730f0e 100644
--- a/target/product/updatable_apex.mk
+++ b/target/product/updatable_apex.mk
@@ -18,6 +18,6 @@
 
 ifneq ($(OVERRIDE_TARGET_FLATTEN_APEX),true)
   PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt
-  PRODUCT_PROPERTY_OVERRIDES := ro.apex.updatable=true
+  PRODUCT_VENDOR_PROPERTIES := ro.apex.updatable=true
   TARGET_FLATTEN_APEX := false
 endif
diff --git a/target/product/userspace_reboot.mk b/target/product/userspace_reboot.mk
index 3f881af..f235d14 100644
--- a/target/product/userspace_reboot.mk
+++ b/target/product/userspace_reboot.mk
@@ -16,4 +16,4 @@
 
 # Inherit this when the target supports userspace reboot
 
-PRODUCT_PROPERTY_OVERRIDES := init.userspace_reboot.is_supported=true
+PRODUCT_VENDOR_PROPERTIES := init.userspace_reboot.is_supported=true
diff --git a/target/product/virtual_ab_ota.mk b/target/product/virtual_ab_ota.mk
index 1774de4..e4c4575 100644
--- a/target/product/virtual_ab_ota.mk
+++ b/target/product/virtual_ab_ota.mk
@@ -16,6 +16,6 @@
 
 PRODUCT_VIRTUAL_AB_OTA := true
 
-PRODUCT_PROPERTY_OVERRIDES += ro.virtual_ab.enabled=true
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true
 
 PRODUCT_PACKAGES += e2fsck_ramdisk
diff --git a/target/product/virtual_ab_ota_plus_non_ab.mk b/target/product/virtual_ab_ota_plus_non_ab.mk
index 325d75e..99a10ed 100644
--- a/target/product/virtual_ab_ota_plus_non_ab.mk
+++ b/target/product/virtual_ab_ota_plus_non_ab.mk
@@ -18,4 +18,4 @@
 
 PRODUCT_OTA_FORCE_NON_AB_PACKAGE := true
 
-PRODUCT_PROPERTY_OVERRIDES += ro.virtual_ab.allow_non_ab=true
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.allow_non_ab=true
diff --git a/target/product/virtual_ab_ota_retrofit.mk b/target/product/virtual_ab_ota_retrofit.mk
index 3e85741..3416a4f 100644
--- a/target/product/virtual_ab_ota_retrofit.mk
+++ b/target/product/virtual_ab_ota_retrofit.mk
@@ -18,4 +18,4 @@
 
 PRODUCT_VIRTUAL_AB_OTA_RETROFIT := true
 
-PRODUCT_PROPERTY_OVERRIDES += ro.virtual_ab.retrofit=true
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.retrofit=true
diff --git a/tools/Android.bp b/tools/Android.bp
index 159890c..149d06d 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -37,3 +37,22 @@
     },
   },
 }
+
+python_test_host {
+  name: "post_process_props_unittest",
+  main: "test_post_process_props.py",
+  srcs: [
+    "post_process_props.py",
+    "test_post_process_props.py",
+  ],
+  version: {
+    py2: {
+      enabled: false,
+    },
+    py3: {
+      enabled: true,
+    },
+  },
+  test_config: "post_process_props_unittest.xml",
+  test_suites: ["general-tests"],
+}
diff --git a/tools/post_process_props.py b/tools/post_process_props.py
index 4fa15bc..d8c9cb1 100755
--- a/tools/post_process_props.py
+++ b/tools/post_process_props.py
@@ -14,10 +14,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import argparse
 import sys
 
-# Usage: post_process_props.py file.prop [blacklist_key, ...]
-# Blacklisted keys are removed from the property file, if present
+# Usage: post_process_props.py file.prop [disallowed_key, ...]
+# Disallowed keys are removed from the property file, if present
 
 # See PROP_VALUE_MAX in system_properties.h.
 # The constant in system_properties.h includes the terminating NUL,
@@ -29,8 +30,8 @@
 def mangle_build_prop(prop_list):
   # If ro.debuggable is 1, then enable adb on USB by default
   # (this is for userdebug builds)
-  if prop_list.get("ro.debuggable") == "1":
-    val = prop_list.get("persist.sys.usb.config")
+  if prop_list.get_value("ro.debuggable") == "1":
+    val = prop_list.get_value("persist.sys.usb.config")
     if "adb" not in val:
       if val == "":
         val = "adb"
@@ -40,52 +41,132 @@
   # UsbDeviceManager expects a value here.  If it doesn't get it, it will
   # default to "adb". That might not the right policy there, but it's better
   # to be explicit.
-  if not prop_list.get("persist.sys.usb.config"):
+  if not prop_list.get_value("persist.sys.usb.config"):
     prop_list.put("persist.sys.usb.config", "none");
 
 def validate(prop_list):
   """Validate the properties.
 
+  If the value of a sysprop exceeds the max limit (91), it's an error, unless
+  the sysprop is a read-only one.
+
+  Checks if there is no optional prop assignments.
+
   Returns:
     True if nothing is wrong.
   """
   check_pass = True
-  for p in prop_list.get_all():
+  for p in prop_list.get_all_props():
     if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."):
       check_pass = False
       sys.stderr.write("error: %s cannot exceed %d bytes: " %
                        (p.name, PROP_VALUE_MAX))
       sys.stderr.write("%s (%d)\n" % (p.value, len(p.value)))
+
+    if p.is_optional():
+      check_pass = False
+      sys.stderr.write("error: found unresolved optional prop assignment:\n")
+      sys.stderr.write(str(p) + "\n")
+
   return check_pass
 
+def override_optional_props(prop_list, allow_dup=False):
+  """Override a?=b with a=c, if the latter exists
+
+  Overriding is done by deleting a?=b
+  When there are a?=b and a?=c, then only the last one survives
+  When there are a=b and a=c, then it's an error.
+
+  Returns:
+    True if the override was successful
+  """
+  success = True
+  for name in prop_list.get_all_names():
+    props = prop_list.get_props(name)
+    optional_props = [p for p in props if p.is_optional()]
+    overriding_props = [p for p in props if not p.is_optional()]
+    if len(overriding_props) > 1:
+      # duplicated props are allowed when the all have the same value
+      if all(overriding_props[0].value == p.value for p in overriding_props):
+        for p in optional_props:
+          p.delete("overridden by %s" % str(overriding_props[0]))
+        continue
+      # or if dup is explicitly allowed for compat reason
+      if allow_dup:
+        # this could left one or more optional props unresolved.
+        # Convert them into non-optional because init doesn't understand ?=
+        # syntax
+        for p in optional_props:
+          p.optional = False
+        continue
+
+      success = False
+      sys.stderr.write("error: found duplicate sysprop assignments:\n")
+      for p in overriding_props:
+        sys.stderr.write("%s\n" % str(p))
+    elif len(overriding_props) == 1:
+      for p in optional_props:
+        p.delete("overridden by %s" % str(overriding_props[0]))
+    else:
+      if len(optional_props) > 1:
+        for p in optional_props[:-1]:
+          p.delete("overridden by %s" % str(optional_props[-1]))
+      # Make the last optional one as non-optional
+      optional_props[-1].optional = False
+
+  return success
+
 class Prop:
 
-  def __init__(self, name, value, comment=None):
+  def __init__(self, name, value, optional=False, comment=None):
     self.name = name.strip()
     self.value = value.strip()
-    self.comment = comment
+    if comment != None:
+      self.comments = [comment]
+    else:
+      self.comments = []
+    self.optional = optional
 
   @staticmethod
   def from_line(line):
     line = line.rstrip('\n')
     if line.startswith("#"):
-      return Prop("", "", line)
+      return Prop("", "", comment=line)
+    elif "?=" in line:
+      name, value = line.split("?=", 1)
+      return Prop(name, value, optional=True)
     elif "=" in line:
       name, value = line.split("=", 1)
-      return Prop(name, value)
+      return Prop(name, value, optional=False)
     else:
       # don't fail on invalid line
       # TODO(jiyong) make this a hard error
-      return Prop("", "", line)
+      return Prop("", "", comment=line)
 
   def is_comment(self):
-    return self.comment != None
+    return bool(self.comments and not self.name)
+
+  def is_optional(self):
+    return (not self.is_comment()) and self.optional
+
+  def make_as_comment(self):
+    # Prepend "#" to the last line which is the prop assignment
+    if not self.is_comment():
+      assignment = str(self).rsplit("\n", 1)[-1]
+      self.comments.append("#" + assignment)
+      self.name = ""
+      self.value = ""
+
+  def delete(self, reason):
+    self.comments.append("# Removed by post_process_props.py because " + reason)
+    self.make_as_comment()
 
   def __str__(self):
-    if self.is_comment():
-      return self.comment
-    else:
-      return self.name + "=" + self.value
+    assignment = []
+    if not self.is_comment():
+      operator = "?=" if self.is_optional() else "="
+      assignment.append(self.name + operator + self.value)
+    return "\n".join(self.comments + assignment)
 
 class PropList:
 
@@ -94,47 +175,65 @@
       self.props = [Prop.from_line(l)
                     for l in f.readlines() if l.strip() != ""]
 
-  def get_all(self):
+  def get_all_props(self):
     return [p for p in self.props if not p.is_comment()]
 
-  def get(self, name):
+  def get_all_names(self):
+    return set([p.name for p in self.get_all_props()])
+
+  def get_props(self, name):
+    return [p for p in self.get_all_props() if p.name == name]
+
+  def get_value(self, name):
+    # Caution: only the value of the first sysprop having the name is returned.
     return next((p.value for p in self.props if p.name == name), "")
 
   def put(self, name, value):
-    index = next((i for i,p in enumerate(self.props) if p.name == name), -1)
+    # Note: when there is an optional prop for the name, its value isn't changed.
+    # Instead a new non-optional prop is appended, which will override the
+    # optional prop. Otherwise, the new value might be overridden by an existing
+    # non-optional prop of the same name.
+    index = next((i for i,p in enumerate(self.props)
+                  if p.name == name and not p.is_optional()), -1)
     if index == -1:
-      self.props.append(Prop(name, value))
+      self.props.append(Prop(name, value,
+                             comment="# Auto-added by post_process_props.py"))
     else:
+      self.props[index].comments.append(
+          "# Value overridden by post_process_props.py. Original value: %s" %
+          self.props[index].value)
       self.props[index].value = value
 
-  def delete(self, name):
-    index = next((i for i,p in enumerate(self.props) if p.name == name), -1)
-    if index != -1:
-      new_comment = "# removed by post_process_props.py\n#" + str(self.props[index])
-      self.props[index] = Prop.from_line(new_comment)
-
   def write(self, filename):
     with open(filename, 'w+') as f:
       for p in self.props:
         f.write(str(p) + "\n")
 
 def main(argv):
-  filename = argv[1]
+  parser = argparse.ArgumentParser(description="Post-process build.prop file")
+  parser.add_argument("--allow-dup", dest="allow_dup", action="store_true",
+                      default=False)
+  parser.add_argument("filename")
+  parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*")
+  args = parser.parse_args()
 
-  if not filename.endswith("/build.prop"):
+  if not args.filename.endswith("/build.prop"):
     sys.stderr.write("bad command line: " + str(argv) + "\n")
     sys.exit(1)
 
-  props = PropList(filename)
+  props = PropList(args.filename)
   mangle_build_prop(props)
+  if not override_optional_props(props, args.allow_dup):
+    sys.exit(1)
   if not validate(props):
     sys.exit(1)
 
-  # Drop any blacklisted keys
-  for key in argv[2:]:
-    props.delete(key)
+  # Drop any disallowed keys
+  for key in args.disallowed_keys:
+    for p in props.get_props(key):
+      p.delete("%s is a disallowed key" % key)
 
-  props.write(filename)
+  props.write(args.filename)
 
 if __name__ == "__main__":
   main(sys.argv)
diff --git a/tools/post_process_props_unittest.xml b/tools/post_process_props_unittest.xml
new file mode 100644
index 0000000..4a6ecc2
--- /dev/null
+++ b/tools/post_process_props_unittest.xml
@@ -0,0 +1,6 @@
+<configuration description="Config to run post_process_props_unittest">
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="post_process_props_unittest" />
+        <option name="test-timeout" value="1m" />
+    </test>
+</configuration>
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index b52da87..11f92ab 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -184,6 +184,7 @@
         "bsdiff",
         "imgdiff",
         "minigzip",
+        "lz4",
         "mkbootfs",
         "signapk",
     ],
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
old mode 100755
new mode 100644
index 490b44a..723fe94
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -741,23 +741,32 @@
   # target_files.zip as a prebuilt blob. We consider either of them as
   # {vendor,product,system_ext}.img being available, which could be
   # used when generating vbmeta.img for AVB.
-  has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
-                os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
-                                            "vendor.img")))
-  has_odm = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) or
-             os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
-                                         "odm.img")))
-  has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or
-                 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
-                                             "product.img")))
-  has_system_ext = (os.path.isdir(os.path.join(OPTIONS.input_tmp,
-                                               "SYSTEM_EXT")) or
-                    os.path.exists(os.path.join(OPTIONS.input_tmp,
-                                                "IMAGES",
-                                                "system_ext.img")))
-  has_system = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM"))
-  has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
-                                                "SYSTEM_OTHER"))
+  has_vendor = ((os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) and
+                 OPTIONS.info_dict.get("building_vendor_image") == "true") or
+                os.path.exists(
+                    os.path.join(OPTIONS.input_tmp, "IMAGES", "vendor.img")))
+  has_odm = ((os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) and
+              OPTIONS.info_dict.get("building_odm_image") == "true") or
+             os.path.exists(
+                 os.path.join(OPTIONS.input_tmp, "IMAGES", "odm.img")))
+  has_product = ((os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) and
+                  OPTIONS.info_dict.get("building_product_image") == "true") or
+                 os.path.exists(
+                     os.path.join(OPTIONS.input_tmp, "IMAGES", "product.img")))
+  has_system_ext = (
+      (os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM_EXT")) and
+       OPTIONS.info_dict.get("building_system_ext_image") == "true") or
+      os.path.exists(
+          os.path.join(OPTIONS.input_tmp, "IMAGES", "system_ext.img")))
+  has_system = (
+      os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM")) and
+      OPTIONS.info_dict.get("building_system_image") == "true")
+
+  has_system_other = (
+      os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM_OTHER")) and
+      OPTIONS.info_dict.get("building_system_other_image") == "true")
+  has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
+  has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
 
   # Set up the output destination. It writes to the given directory for dir
   # mode; otherwise appends to the given ZIP.
@@ -788,16 +797,16 @@
     boot_images = OPTIONS.info_dict.get("boot_images")
     if boot_images is None:
       boot_images = "boot.img"
-    for b in boot_images.split():
+    for index,b in enumerate(boot_images.split()):
       # common.GetBootableImage() returns the image directly if present.
       boot_image = common.GetBootableImage(
           "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
       # boot.img may be unavailable in some targets (e.g. aosp_arm64).
       if boot_image:
         boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
-        # vbmeta does not need to include boot.img with multiple boot.img files,
-        # which is only used for aosp_arm64 for GKI
-        if len(boot_images.split()) == 1:
+        # Although multiple boot images can be generated, include the image
+        # descriptor of only the first boot image in vbmeta
+        if index == 0:
           partitions['boot'] = boot_image_path
         if not os.path.exists(boot_image_path):
           boot_image.WriteToDir(OPTIONS.input_tmp)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index bc6eb24..8bbc35e 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -17,6 +17,7 @@
 import base64
 import collections
 import copy
+import datetime
 import errno
 import fnmatch
 import getopt
@@ -53,16 +54,17 @@
     # running this function, user-supplied search path (`--path`) hasn't been
     # available. So the value set here is the default, which might be overridden
     # by commandline flag later.
-    exec_path = sys.argv[0]
+    exec_path = os.path.realpath(sys.argv[0])
     if exec_path.endswith('.py'):
       script_name = os.path.basename(exec_path)
       # logger hasn't been initialized yet at this point. Use print to output
       # warnings.
       print(
           'Warning: releasetools script should be invoked as hermetic Python '
-          'executable -- build and run `{}` directly.'.format(script_name[:-3]),
+          'executable -- build and run `{}` directly.'.format(
+              script_name[:-3]),
           file=sys.stderr)
-    self.search_path = os.path.realpath(os.path.join(os.path.dirname(exec_path), '..'))
+    self.search_path = os.path.dirname(os.path.dirname(exec_path))
 
     self.signapk_path = "framework/signapk.jar"  # Relative to search_path
     self.signapk_shared_library_path = "lib64"   # Relative to search_path
@@ -191,11 +193,11 @@
     if OPTIONS.logfile:
       config = copy.deepcopy(config)
       config['handlers']['logfile'] = {
-        'class': 'logging.FileHandler',
-        'formatter': 'standard',
-        'level': 'INFO',
-        'mode': 'w',
-        'filename': OPTIONS.logfile,
+          'class': 'logging.FileHandler',
+          'formatter': 'standard',
+          'level': 'INFO',
+          'mode': 'w',
+          'filename': OPTIONS.logfile,
       }
       config['loggers']['']['handlers'].append('logfile')
 
@@ -224,7 +226,7 @@
   if 'universal_newlines' not in kwargs:
     kwargs['universal_newlines'] = True
   # Don't log any if caller explicitly says so.
-  if verbose != False:
+  if verbose:
     logger.info("  Running: \"%s\"", " ".join(args))
   return subprocess.Popen(args, **kwargs)
 
@@ -274,7 +276,7 @@
   if output is None:
     output = ""
   # Don't log any if caller explicitly says so.
-  if verbose != False:
+  if verbose:
     logger.info("%s", output.rstrip())
   if proc.returncode != 0:
     raise ExternalError(
@@ -375,7 +377,6 @@
             'Invalid build fingerprint: "{}". See the requirement in Android CDD '
             "3.2.2. Build Parameters.".format(fingerprint))
 
-
     self._partition_fingerprints = {}
     for partition in PARTITIONS_WITH_CARE_MAP:
       try:
@@ -522,7 +523,8 @@
           self.GetPartitionBuildProp("ro.product.device", partition),
           self.GetPartitionBuildProp("ro.build.version.release", partition),
           self.GetPartitionBuildProp("ro.build.id", partition),
-          self.GetPartitionBuildProp("ro.build.version.incremental", partition),
+          self.GetPartitionBuildProp(
+              "ro.build.version.incremental", partition),
           self.GetPartitionBuildProp("ro.build.type", partition),
           self.GetPartitionBuildProp("ro.build.tags", partition))
 
@@ -683,7 +685,7 @@
   if "boot_images" in d:
     boot_images = d["boot_images"]
   for b in boot_images.split():
-    makeint(b.replace(".img","_size"))
+    makeint(b.replace(".img", "_size"))
 
   # Load recovery fstab if applicable.
   d["fstab"] = _FindAndLoadRecoveryFstab(d, input_file, read_helper)
@@ -749,6 +751,7 @@
         placeholders in the build.prop file. We expect exactly one value for
         each of the variables.
   """
+
   def __init__(self, input_file, name, placeholder_values=None):
     self.input_file = input_file
     self.partition = name
@@ -808,7 +811,7 @@
     """Parses the build prop in a given import statement."""
 
     tokens = line.split()
-    if tokens[0] != 'import' or (len(tokens) != 2 and len(tokens) != 3) :
+    if tokens[0] != 'import' or (len(tokens) != 2 and len(tokens) != 3):
       raise ValueError('Unrecognized import statement {}'.format(line))
 
     if len(tokens) == 3:
@@ -998,9 +1001,9 @@
 
   # Pick virtual ab related flags from vendor dict, if defined.
   if "virtual_ab" in vendor_dict.keys():
-     merged_dict["virtual_ab"] = vendor_dict["virtual_ab"]
+    merged_dict["virtual_ab"] = vendor_dict["virtual_ab"]
   if "virtual_ab_retrofit" in vendor_dict.keys():
-     merged_dict["virtual_ab_retrofit"] = vendor_dict["virtual_ab_retrofit"]
+    merged_dict["virtual_ab_retrofit"] = vendor_dict["virtual_ab_retrofit"]
   return merged_dict
 
 
@@ -1191,7 +1194,7 @@
     AddAftlInclusionProof(image_path)
 
 
-def _MakeRamdisk(sourcedir, fs_config_file=None):
+def _MakeRamdisk(sourcedir, fs_config_file=None, lz4_ramdisks=False):
   ramdisk_img = tempfile.NamedTemporaryFile()
 
   if fs_config_file is not None and os.access(fs_config_file, os.F_OK):
@@ -1200,12 +1203,16 @@
   else:
     cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
   p1 = Run(cmd, stdout=subprocess.PIPE)
-  p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
+  if lz4_ramdisks:
+    p2 = Run(["lz4", "-l", "-12" , "--favor-decSpeed"], stdin=p1.stdout,
+             stdout=ramdisk_img.file.fileno())
+  else:
+    p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
 
   p2.wait()
   p1.wait()
   assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
-  assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
+  assert p2.returncode == 0, "compression of %s ramdisk failed" % (sourcedir,)
 
   return ramdisk_img
 
@@ -1230,7 +1237,7 @@
     kernel = "kernel"
   else:
     kernel = image_name.replace("boot", "kernel")
-    kernel = kernel.replace(".img","")
+    kernel = kernel.replace(".img", "")
   if not os.access(os.path.join(sourcedir, kernel), os.F_OK):
     return None
 
@@ -1243,7 +1250,8 @@
   img = tempfile.NamedTemporaryFile()
 
   if has_ramdisk:
-    ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file)
+    use_lz4 = info_dict.get("lz4_ramdisks") == 'true'
+    ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file, lz4_ramdisks=use_lz4)
 
   # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
   mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
@@ -1353,7 +1361,7 @@
     if partition_name == "recovery":
       part_size = info_dict["recovery_size"]
     else:
-      part_size = info_dict[image_name.replace(".img","_size")]
+      part_size = info_dict[image_name.replace(".img", "_size")]
     cmd = [avbtool, "add_hash_footer", "--image", img.name,
            "--partition_size", str(part_size), "--partition_name",
            partition_name]
@@ -1427,7 +1435,8 @@
 
   img = tempfile.NamedTemporaryFile()
 
-  ramdisk_img = _MakeRamdisk(sourcedir)
+  use_lz4 = info_dict.get("lz4_ramdisks") == 'true'
+  ramdisk_img = _MakeRamdisk(sourcedir, lz4_ramdisks=use_lz4)
 
   # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
   mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
@@ -1505,7 +1514,8 @@
   if info_dict is None:
     info_dict = OPTIONS.info_dict
 
-  data = _BuildVendorBootImage(os.path.join(unpack_dir, tree_subdir), info_dict)
+  data = _BuildVendorBootImage(
+      os.path.join(unpack_dir, tree_subdir), info_dict)
   if data:
     return File(name, data)
   return None
@@ -1514,7 +1524,7 @@
 def Gunzip(in_filename, out_filename):
   """Gunzips the given gzip compressed file to a given output file."""
   with gzip.open(in_filename, "rb") as in_file, \
-       open(out_filename, "wb") as out_file:
+          open(out_filename, "wb") as out_file:
     shutil.copyfileobj(in_file, out_file)
 
 
@@ -1616,8 +1626,7 @@
     if reset_file_map:
       img.ResetFileMap()
     return img
-  else:
-    return GetNonSparseImage(which, tmpdir, hashtree_info_generator)
+  return GetNonSparseImage(which, tmpdir, hashtree_info_generator)
 
 
 def GetNonSparseImage(which, tmpdir, hashtree_info_generator=None):
@@ -1816,10 +1825,9 @@
     # Not a decimal number. Codename?
     if version in codename_to_api_level_map:
       return codename_to_api_level_map[version]
-    else:
-      raise ExternalError(
-          "Unknown minSdkVersion: '{}'. Known codenames: {}".format(
-              version, codename_to_api_level_map))
+    raise ExternalError(
+        "Unknown minSdkVersion: '{}'. Known codenames: {}".format(
+            version, codename_to_api_level_map))
 
 
 def SignFile(input_name, output_name, key, password, min_api_level=None,
@@ -1924,7 +1932,8 @@
     msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
     if pct >= 99.0:
       raise ExternalError(msg)
-    elif pct >= 95.0:
+
+    if pct >= 95.0:
       logger.warning("\n  WARNING: %s\n", msg)
     else:
       logger.info("  %s", msg)
@@ -2034,6 +2043,7 @@
       Put verbose logs to specified file (regardless of --verbose option.)
 """
 
+
 def Usage(docstring):
   print(docstring.rstrip("\n"))
   print(COMMON_DOCSTRING)
@@ -2196,7 +2206,7 @@
 
       current = self.UpdateAndReadFile(current)
 
-  def PromptResult(self, current): # pylint: disable=no-self-use
+  def PromptResult(self, current):  # pylint: disable=no-self-use
     """Prompt the user to enter a value (password) for each key in
     'current' whose value is fales.  Returns a new dict with all the
     values.
@@ -2259,7 +2269,6 @@
 
 def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
              compress_type=None):
-  import datetime
 
   # http://b/18015246
   # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
@@ -2385,6 +2394,7 @@
 
 class DeviceSpecificParams(object):
   module = None
+
   def __init__(self, **kwargs):
     """Keyword arguments to the constructor become attributes of this
     object, which is passed to all functions in the device-specific
@@ -2513,12 +2523,12 @@
 
 
 DIFF_PROGRAM_BY_EXT = {
-    ".gz" : "imgdiff",
-    ".zip" : ["imgdiff", "-z"],
-    ".jar" : ["imgdiff", "-z"],
-    ".apk" : ["imgdiff", "-z"],
-    ".img" : "imgdiff",
-    }
+    ".gz": "imgdiff",
+    ".zip": ["imgdiff", "-z"],
+    ".jar": ["imgdiff", "-z"],
+    ".apk": ["imgdiff", "-z"],
+    ".img": "imgdiff",
+}
 
 
 class Difference(object):
@@ -2557,6 +2567,7 @@
       cmd.append(ptemp.name)
       p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
       err = []
+
       def run():
         _, e = p.communicate()
         if e:
@@ -2585,7 +2596,6 @@
     self.patch = diff
     return self.tf, self.sf, self.patch
 
-
   def GetPatch(self):
     """Returns a tuple of (target_file, source_file, patch_data).
 
@@ -2896,7 +2906,7 @@
                 new_data_name=new_data_name, code=code))
     script.AppendExtra(script.WordWrap(call))
 
-  def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
+  def _HashBlocks(self, source, ranges):  # pylint: disable=no-self-use
     data = source.ReadRangeSet(ranges)
     ctx = sha1()
 
@@ -2905,7 +2915,7 @@
 
     return ctx.hexdigest()
 
-  def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
+  def _HashZeroBlocks(self, num_blocks):  # pylint: disable=no-self-use
     """Return the hash value for all zero blocks."""
     zero_block = '\x00' * 4096
     ctx = sha1()
@@ -2928,6 +2938,7 @@
     "squashfs": "EMMC"
 }
 
+
 def GetTypeAndDevice(mount_point, info, check_no_slot=True):
   """
   Use GetTypeAndDeviceExpr whenever possible. This function is kept for
@@ -2938,11 +2949,10 @@
   if fstab:
     if check_no_slot:
       assert not fstab[mount_point].slotselect, \
-             "Use GetTypeAndDeviceExpr instead"
+          "Use GetTypeAndDeviceExpr instead"
     return (PARTITION_TYPES[fstab[mount_point].fs_type],
             fstab[mount_point].device)
-  else:
-    raise KeyError
+  raise KeyError
 
 
 def GetTypeAndDeviceExpr(mount_point, info):
@@ -2957,8 +2967,7 @@
     if p.slotselect:
       device_expr = 'add_slot_suffix(%s)' % device_expr
     return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr)
-  else:
-    raise KeyError
+  raise KeyError
 
 
 def GetEntryForDevice(fstab, device):
@@ -2973,6 +2982,7 @@
       return fstab[mount_point]
   return None
 
+
 def ParseCertificate(data):
   """Parses and converts a PEM-encoded certificate into DER-encoded.
 
@@ -3299,7 +3309,7 @@
       for p, u in self._partition_updates.items():
         if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
           u.block_difference.WritePostInstallVerifyScript(script)
-          script.AppendExtra('unmap_partition("%s");' % p) # ignore errors
+          script.AppendExtra('unmap_partition("%s");' % p)  # ignore errors
 
     for p, u in self._partition_updates.items():
       if u.tgt_size and u.src_size <= u.tgt_size:
@@ -3307,7 +3317,7 @@
         u.block_difference.WriteScript(script, output_zip, progress=u.progress,
                                        write_verify_script=write_verify_script)
         if write_verify_script:
-          script.AppendExtra('unmap_partition("%s");' % p) # ignore errors
+          script.AppendExtra('unmap_partition("%s");' % p)  # ignore errors
 
     script.Comment('--- End patching dynamic partitions ---')
 
@@ -3364,7 +3374,8 @@
 
     for p, u in self._partition_updates.items():
       if u.tgt_size and u.src_size < u.tgt_size:
-        comment('Grow partition %s from %d to %d' % (p, u.src_size, u.tgt_size))
+        comment('Grow partition %s from %d to %d' %
+                (p, u.src_size, u.tgt_size))
         append('resize %s %d' % (p, u.tgt_size))
 
     for p, u in self._partition_updates.items():
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 99e21f1..b9c9b19 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -374,12 +374,12 @@
 
   def _CheckSecondTokenNotSlotSuffixed(self, s, fn):
     lst = s.split(':')
-    assert(len(s) == 4), "{} does not contain 4 tokens".format(s)
+    assert(len(lst) == 4), "{} does not contain 4 tokens".format(s)
     if self.fstab:
-      entry = common.GetEntryForDevice(s[1])
+      entry = common.GetEntryForDevice(self.fstab, lst[1])
       if entry is not None:
         assert not entry.slotselect, \
-          "Use %s because %s is slot suffixed" % (fn, s[1])
+          "Use %s because %s is slot suffixed" % (fn, lst[1])
 
   def WriteRawImage(self, mount_point, fn, mapfn=None):
     """Write the given package file into the partition for the given
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index d9d3854..ed42b20 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -175,6 +175,9 @@
     'ab_update',
     'default_system_dev_certificate',
     'system_size',
+    'building_system_image',
+    'building_system_ext_image',
+    'building_product_image',
 )
 
 # DEFAULT_VENDOR_ITEM_LIST is a list of items to extract from the partial
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index b753414..7fb0a77 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -221,8 +221,10 @@
 import check_target_files_vintf
 import common
 import edify_generator
+import target_files_diff
 import verity_utils
 
+
 if sys.hexversion < 0x02070000:
   print("Python 2.7 or newer is required.", file=sys.stderr)
   sys.exit(1)
@@ -545,10 +547,10 @@
     target_files_dir = "SYSTEM/vendor"
 
   patch = "%s/recovery-from-boot.p" % target_files_dir
-  img = "%s/etc/recovery.img" %target_files_dir
+  img = "%s/etc/recovery.img" % target_files_dir
 
-  namelist = [name for name in target_files_zip.namelist()]
-  return (patch in namelist or img in namelist)
+  namelist = target_files_zip.namelist()
+  return patch in namelist or img in namelist
 
 
 def HasPartition(target_files_zip, partition):
@@ -626,7 +628,8 @@
 
   def GetIncrementalBlockDifferenceForPartition(name):
     if not HasPartition(source_zip, name):
-      raise RuntimeError("can't generate incremental that adds {}".format(name))
+      raise RuntimeError(
+          "can't generate incremental that adds {}".format(name))
 
     partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip,
                                         info_dict=source_info,
@@ -637,8 +640,7 @@
     partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip,
                                         info_dict=target_info,
                                         allow_shared_blocks=allow_shared_blocks,
-                                        hashtree_info_generator=
-                                        hashtree_info_generator)
+                                        hashtree_info_generator=hashtree_info_generator)
 
     # Check the first block of the source system partition for remount R/W only
     # if the filesystem is ext4.
@@ -1450,7 +1452,7 @@
     fs = source_info["fstab"]["/misc"]
     assert fs.fs_type.upper() == "EMMC", \
         "two-step packages only supported on devices with EMMC /misc partitions"
-    bcb_dev = {"bcb_dev" : fs.device}
+    bcb_dev = {"bcb_dev": fs.device}
     common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
     script.AppendExtra("""
 if get_stage("%(bcb_dev)s") == "2/3" then
@@ -1668,7 +1670,7 @@
         partitions = [partition for partition in partitions if partition
                       not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
         output_list.append('{}={}'.format(key, ' '.join(partitions)))
-      elif key == 'virtual_ab' or key == "virtual_ab_retrofit":
+      elif key in ['virtual_ab', "virtual_ab_retrofit"]:
         # Remove virtual_ab flag from secondary payload so that OTA client
         # don't use snapshots for secondary update
         pass
@@ -1712,7 +1714,8 @@
           partition_list = f.read().splitlines()
         partition_list = [partition for partition in partition_list if partition
                           and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
-        common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list))
+        common.ZipWriteStr(target_zip, info.filename,
+                           '\n'.join(partition_list))
       # Remove the unnecessary partitions from the dynamic partitions list.
       elif (info.filename == 'META/misc_info.txt' or
             info.filename == DYNAMIC_PARTITION_INFO):
@@ -1795,7 +1798,8 @@
       "{} is in super_block_devices but not in {}".format(
           super_device_not_updated, AB_PARTITIONS)
   # ab_partitions -= (dynamic_partition_list - super_block_devices)
-  new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
+  new_ab_partitions = common.MakeTempFile(
+      prefix="ab_partitions", suffix=".txt")
   with open(new_ab_partitions, 'w') as f:
     for partition in ab_partitions:
       if (partition in dynamic_partition_list and
@@ -1985,7 +1989,7 @@
     OPTIONS.source_tmp = common.UnzipTemp(
         OPTIONS.incremental_source, UNZIP_PATTERN)
     with zipfile.ZipFile(target_file) as input_zip, \
-        zipfile.ZipFile(source_file) as source_zip:
+            zipfile.ZipFile(source_file) as source_zip:
       WriteBlockIncrementalOTAPackage(
           input_zip,
           source_zip,
@@ -2012,9 +2016,16 @@
     info_dict = copy.deepcopy(build_info.info_dict)
     for partition in common.PARTITIONS_WITH_CARE_MAP:
       partition_prop_key = "{}.build.prop".format(partition)
-      old_props = info_dict[partition_prop_key]
-      info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
-          old_props.input_file, partition, placeholder_values)
+      input_file = info_dict[partition_prop_key].input_file
+      if isinstance(input_file, zipfile.ZipFile):
+        with zipfile.ZipFile(input_file.filename) as input_zip:
+          info_dict[partition_prop_key] = \
+              common.PartitionBuildProps.FromInputFile(input_zip, partition,
+                                                       placeholder_values)
+      else:
+        info_dict[partition_prop_key] = \
+            common.PartitionBuildProps.FromInputFile(input_file, partition,
+                                                     placeholder_values)
     info_dict["build.prop"] = info_dict["system.build.prop"]
 
     new_build_info = common.BuildInfo(info_dict, build_info.oem_dicts)
@@ -2238,7 +2249,6 @@
         OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
 
     with open(OPTIONS.log_diff, 'w') as out_file:
-      import target_files_diff
       target_files_diff.recursiveDiff(
           '', source_dir, target_dir, out_file)
 
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 47360c9..5d10c40 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -383,8 +383,8 @@
 
 
 def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
-            is_compressed):
-  unsigned = tempfile.NamedTemporaryFile()
+            is_compressed, apk_name):
+  unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
   unsigned.write(data)
   unsigned.flush()
 
@@ -402,7 +402,7 @@
     unsigned.close()
     unsigned = uncompressed
 
-  signed = tempfile.NamedTemporaryFile()
+  signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
 
   # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
   # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
@@ -488,7 +488,7 @@
       if key not in common.SPECIAL_CERT_STRINGS:
         print("    signing: %-*s (%s)" % (maxsize, name, key))
         signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
-                              codename_to_api_level_map, is_compressed)
+                              codename_to_api_level_map, is_compressed, name)
         common.ZipWriteStr(output_tf_zip, out_info, signed_data)
       else:
         # an APK we're not supposed to sign.
@@ -608,22 +608,22 @@
     elif (OPTIONS.remove_avb_public_keys and
           (filename.startswith("BOOT/RAMDISK/avb/") or
            filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
-        matched_removal = False
-        for key_to_remove in OPTIONS.remove_avb_public_keys:
-          if filename.endswith(key_to_remove):
-            matched_removal = True
-            print("Removing AVB public key from ramdisk: %s" % filename)
-            break
-        if not matched_removal:
-          # Copy it verbatim if we don't want to remove it.
-          common.ZipWriteStr(output_tf_zip, out_info, data)
+      matched_removal = False
+      for key_to_remove in OPTIONS.remove_avb_public_keys:
+        if filename.endswith(key_to_remove):
+          matched_removal = True
+          print("Removing AVB public key from ramdisk: %s" % filename)
+          break
+      if not matched_removal:
+        # Copy it verbatim if we don't want to remove it.
+        common.ZipWriteStr(output_tf_zip, out_info, data)
 
     # Skip verity keyid (for system_root_image use) if we will replace it.
     elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
       pass
 
     # Skip the care_map as we will regenerate the system/vendor images.
-    elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
+    elif filename in ["META/care_map.pb", "META/care_map.txt"]:
       pass
 
     # Updates system_other.avbpubkey in /product/etc/.
@@ -967,11 +967,10 @@
     if extra_args:
       print('Setting extra AVB signing args for %s to "%s"' % (
           partition, extra_args))
-      if partition in AVB_FOOTER_ARGS_BY_PARTITION:
-        args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
-      else:
-        # custom partition
-        args_key = "avb_{}_add_hashtree_footer_args".format(partition)
+      args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
+          partition,
+          # custom partition
+          "avb_{}_add_hashtree_footer_args".format(partition))
       misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
 
   for partition in AVB_FOOTER_ARGS_BY_PARTITION:
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 9f4f8a6..8763825 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -371,6 +371,17 @@
             partition, info_dict, key_file)
         cmd.extend(['--expected_chain_partition', chained_partition_arg])
 
+    # Handle the boot image with a non-default name, e.g. boot-5.4.img
+    boot_images = info_dict.get("boot_images")
+    if boot_images:
+      # we used the 1st boot image to generate the vbmeta. Rename the filename
+      # to boot.img so that avbtool can find it correctly.
+      first_image_name = boot_images.split()[0]
+      first_image_path = os.path.join(input_tmp, 'IMAGES', first_image_name)
+      assert os.path.isfile(first_image_path)
+      renamed_boot_image_path = os.path.join(input_tmp, 'IMAGES', 'boot.img')
+      os.rename(first_image_path, renamed_boot_image_path)
+
     proc = common.Run(cmd)
     stdoutdata, _ = proc.communicate()
     assert proc.returncode == 0, \
diff --git a/tools/test_post_process_props.py b/tools/test_post_process_props.py
new file mode 100644
index 0000000..12d52e5
--- /dev/null
+++ b/tools/test_post_process_props.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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 contextlib
+import io
+import unittest
+
+from unittest.mock import *
+from post_process_props import *
+
+class PropTestCase(unittest.TestCase):
+  def test_createFromLine(self):
+    p = Prop.from_line("# this is comment")
+    self.assertTrue(p.is_comment())
+    self.assertEqual("", p.name)
+    self.assertEqual("", p.value)
+    self.assertFalse(p.is_optional())
+    self.assertEqual("# this is comment", str(p))
+
+    for line in ["a=b", "a = b", "a= b", "a =b", "  a=b   "]:
+      p = Prop.from_line(line)
+      self.assertFalse(p.is_comment())
+      self.assertEqual("a", p.name)
+      self.assertEqual("b", p.value)
+      self.assertFalse(p.is_optional())
+      self.assertEqual("a=b", str(p))
+
+    for line in ["a?=b", "a ?= b", "a?= b", "a ?=b", "  a?=b   "]:
+      p = Prop.from_line(line)
+      self.assertFalse(p.is_comment())
+      self.assertEqual("a", p.name)
+      self.assertEqual("b", p.value)
+      self.assertTrue(p.is_optional())
+      self.assertEqual("a?=b", str(p))
+
+  def test_makeAsComment(self):
+    p = Prop.from_line("a=b")
+    p.comments.append("# a comment")
+    self.assertFalse(p.is_comment())
+
+    p.make_as_comment()
+    self.assertTrue(p.is_comment())
+    self.assertTrue("# a comment\n#a=b", str(p))
+
+class PropListTestcase(unittest.TestCase):
+  def setUp(self):
+    content = """
+    # comment
+    foo=true
+    bar=false
+    qux?=1
+    # another comment
+    foo?=false
+    """
+    self.patcher = patch("post_process_props.open", mock_open(read_data=content))
+    self.mock_open = self.patcher.start()
+    self.props = PropList("file")
+
+  def tearDown(self):
+    self.patcher.stop()
+    self.props = None
+
+  def test_readFromFile(self):
+    self.assertEqual(4, len(self.props.get_all_props()))
+    expected = [
+        ("foo", "true", False),
+        ("bar", "false", False),
+        ("qux", "1", True),
+        ("foo", "false", True)
+    ]
+    for i,p in enumerate(self.props.get_all_props()):
+      self.assertEqual(expected[i][0], p.name)
+      self.assertEqual(expected[i][1], p.value)
+      self.assertEqual(expected[i][2], p.is_optional())
+      self.assertFalse(p.is_comment())
+
+    self.assertEqual(set(["foo", "bar", "qux"]), self.props.get_all_names())
+
+    self.assertEqual("true", self.props.get_value("foo"))
+    self.assertEqual("false", self.props.get_value("bar"))
+    self.assertEqual("1", self.props.get_value("qux"))
+
+    # there are two assignments for 'foo'
+    self.assertEqual(2, len(self.props.get_props("foo")))
+
+  def test_putNewProp(self):
+    self.props.put("new", "30")
+
+    self.assertEqual(5, len(self.props.get_all_props()))
+    last_prop = self.props.get_all_props()[-1]
+    self.assertEqual("new", last_prop.name)
+    self.assertEqual("30", last_prop.value)
+    self.assertFalse(last_prop.is_optional())
+
+  def test_putExistingNonOptionalProp(self):
+    self.props.put("foo", "NewValue")
+
+    self.assertEqual(4, len(self.props.get_all_props()))
+    foo_prop = self.props.get_props("foo")[0]
+    self.assertEqual("foo", foo_prop.name)
+    self.assertEqual("NewValue", foo_prop.value)
+    self.assertFalse(foo_prop.is_optional())
+    self.assertEqual("# Value overridden by post_process_props.py. " +
+                     "Original value: true\nfoo=NewValue", str(foo_prop))
+
+  def test_putExistingOptionalProp(self):
+    self.props.put("qux", "2")
+
+    self.assertEqual(5, len(self.props.get_all_props()))
+    last_prop = self.props.get_all_props()[-1]
+    self.assertEqual("qux", last_prop.name)
+    self.assertEqual("2", last_prop.value)
+    self.assertFalse(last_prop.is_optional())
+    self.assertEqual("# Auto-added by post_process_props.py\nqux=2",
+                     str(last_prop))
+
+  def test_deleteNonOptionalProp(self):
+    props_to_delete = self.props.get_props("foo")[0]
+    props_to_delete.delete(reason="testing")
+
+    self.assertEqual(3, len(self.props.get_all_props()))
+    self.assertEqual("# Removed by post_process_props.py because testing\n" +
+                     "#foo=true", str(props_to_delete))
+
+  def test_deleteOptionalProp(self):
+    props_to_delete = self.props.get_props("qux")[0]
+    props_to_delete.delete(reason="testing")
+
+    self.assertEqual(3, len(self.props.get_all_props()))
+    self.assertEqual("# Removed by post_process_props.py because testing\n" +
+                     "#qux?=1", str(props_to_delete))
+
+  def test_overridingNonOptional(self):
+    props_to_be_overridden = self.props.get_props("foo")[1]
+    self.assertTrue("true", props_to_be_overridden.value)
+
+    self.assertTrue(override_optional_props(self.props))
+
+    # size reduced to 3 because foo?=false was overridden by foo=true
+    self.assertEqual(3, len(self.props.get_all_props()))
+
+    self.assertEqual(1, len(self.props.get_props("foo")))
+    self.assertEqual("true", self.props.get_props("foo")[0].value)
+
+    self.assertEqual("# Removed by post_process_props.py because " +
+                     "overridden by foo=true\n#foo?=false",
+                     str(props_to_be_overridden))
+
+  def test_overridingOptional(self):
+    content = """
+    # comment
+    qux?=2
+    foo=true
+    bar=false
+    qux?=1
+    # another comment
+    foo?=false
+    """
+    with patch('post_process_props.open', mock_open(read_data=content)) as m:
+      props = PropList("hello")
+
+      props_to_be_overridden = props.get_props("qux")[0]
+      self.assertEqual("2", props_to_be_overridden.value)
+
+      self.assertTrue(override_optional_props(props))
+
+      self.assertEqual(1, len(props.get_props("qux")))
+      self.assertEqual("1", props.get_props("qux")[0].value)
+      # the only left optional assignment becomes non-optional
+      self.assertFalse(props.get_props("qux")[0].is_optional())
+
+      self.assertEqual("# Removed by post_process_props.py because " +
+                       "overridden by qux?=1\n#qux?=2",
+                       str(props_to_be_overridden))
+
+  def test_overridingDuplicated(self):
+    content = """
+    # comment
+    foo=true
+    bar=false
+    qux?=1
+    foo=false
+    # another comment
+    foo?=false
+    """
+    with patch("post_process_props.open", mock_open(read_data=content)) as m:
+      stderr_redirect = io.StringIO()
+      with contextlib.redirect_stderr(stderr_redirect):
+        props = PropList("hello")
+
+        # fails due to duplicated foo=true and foo=false
+        self.assertFalse(override_optional_props(props))
+
+        self.assertEqual("error: found duplicate sysprop assignments:\n" +
+                         "foo=true\nfoo=false\n", stderr_redirect.getvalue())
+
+  def test_overridingDuplicatedWithSameValue(self):
+    content = """
+    # comment
+    foo=true
+    bar=false
+    qux?=1
+    foo=true
+    # another comment
+    foo?=false
+    """
+    with patch("post_process_props.open", mock_open(read_data=content)) as m:
+      stderr_redirect = io.StringIO()
+      with contextlib.redirect_stderr(stderr_redirect):
+        props = PropList("hello")
+        optional_prop = props.get_props("foo")[2] # the last foo?=false one
+
+        # we have duplicated foo=true and foo=true, but that's allowed
+        # since they have the same value
+        self.assertTrue(override_optional_props(props))
+
+        # foo?=false should be commented out
+        self.assertEqual("# Removed by post_process_props.py because " +
+                         "overridden by foo=true\n#foo?=false",
+                         str(optional_prop))
+
+  def test_allowDuplicates(self):
+    content = """
+    # comment
+    foo=true
+    bar=false
+    qux?=1
+    foo=false
+    # another comment
+    foo?=false
+    """
+    with patch("post_process_props.open", mock_open(read_data=content)) as m:
+      stderr_redirect = io.StringIO()
+      with contextlib.redirect_stderr(stderr_redirect):
+        props = PropList("hello")
+
+        # we have duplicated foo=true and foo=false, but that's allowed
+        # because it's explicitly allowed
+        self.assertTrue(override_optional_props(props, allow_dup=True))
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)