Merge "Compute the needed shared libs for otatools."
diff --git a/core/Makefile b/core/Makefile
index ec0a570..8660984 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -2030,17 +2030,40 @@
 INTERNAL_DEBUG_BOOTIMAGE_ARGS := $(subst $(INSTALLED_RAMDISK_TARGET),$(INSTALLED_DEBUG_RAMDISK_TARGET), $(INTERNAL_BOOTIMAGE_ARGS))
 endif
 
+# If boot.img is chained but boot-debug.img is not signed, libavb in bootloader
+# will fail to find valid AVB metadata from the end of /boot, thus stop booting.
+# Using a test key to sign boot-debug.img to continue booting with the mismatched
+# public key, if the device is unlocked.
+ifneq ($(BOARD_AVB_BOOT_KEY_PATH),)
+BOARD_AVB_DEBUG_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS := \
+  --algorithm SHA256_RSA2048 --key $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
+endif
+
 # Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img
 $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
 	$(call pretty,"Target boot debug image: $@")
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
-	$(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
+	  $(call assert-max-image-size,$@,$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
+	  $(AVBTOOL) add_hash_footer \
+	    --image $@ \
+	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS), \
+	  $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
 
 .PHONY: bootimage_debug-nodeps
 bootimage_debug-nodeps: $(MKBOOTIMG)
 	echo "make $@: ignoring dependencies"
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
-	$(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
+	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
+	  $(AVBTOOL) add_hash_footer \
+	    --image $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
+	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS), \
+	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
 
 endif # TARGET_NO_KERNEL
 
@@ -4242,7 +4265,7 @@
 	$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)
 	$(hide) mkdir -p $(dir $@) $(TARGET_OUT_UNSTRIPPED) $(dir $(PRIVATE_LIST_FILE))
 	$(hide) find -L $(TARGET_OUT_UNSTRIPPED) -type f | sort >$(PRIVATE_LIST_FILE)
-	$(hide) $(SOONG_ZIP) -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE)
+	$(hide) $(SOONG_ZIP) --ignore_missing_files -d -o $@ -C $(OUT_DIR)/.. -l $(PRIVATE_LIST_FILE)
 # -----------------------------------------------------------------
 # A zip of the coverage directory.
 #
diff --git a/core/node_fns.mk b/core/node_fns.mk
index ccfcc25..b81d60c 100644
--- a/core/node_fns.mk
+++ b/core/node_fns.mk
@@ -142,7 +142,8 @@
 #
 # $(1): context prefix
 # $(2): name of this node
-# $(3): list of variable names
+# $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 define _expand-inherited-values
   $(foreach v,$(3), \
@@ -154,15 +155,21 @@
             $(patsubst $(INHERIT_TAG)%,%, \
                 $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \
      )))) \
+    $(eval ### "Whether this variable should only take a single value") \
+    $(eval _eiv_sv := $(filter $(v),$(4))) \
     $(foreach i,$(_eiv_i), \
       $(eval ### "Make sure that this inherit appears only once") \
       $(eval $(_eiv_tv) := \
           $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \
+      $(eval ### "The expanded value, empty if we want a single value and have one") \
+      $(eval _eiv_ev := \
+        $(if $(and $(_eiv_sv),$(filter-out $(INHERIT_TAG)%,$($(_eiv_tv)))),,\
+          $($(1).$(i).$(v)) \
+        ) \
+      ) \
       $(eval ### "Expand the inherit tag") \
       $(eval $(_eiv_tv) := \
-          $(strip \
-              $(patsubst $(INHERIT_TAG)$(i),$($(1).$(i).$(v)), \
-                  $($(_eiv_tv))))) \
+          $(strip $(patsubst $(INHERIT_TAG)$(i),$(_eiv_ev),$($(_eiv_tv))))) \
       $(eval ### "Clear the child so DAGs don't create duplicate entries" ) \
       $(eval $(1).$(i).$(v) :=) \
       $(eval ### "If we just inherited ourselves, it's a cycle.") \
@@ -180,6 +187,7 @@
 # $(1): context prefix
 # $(2): makefile representing this node
 # $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 # _include_stack contains the list of included files, with the most recent files first.
 define _import-node
@@ -198,7 +206,7 @@
       $(call get-inherited-nodes,$(1).$(2),$(3)))
   $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3))
 
-  $(call _expand-inherited-values,$(1),$(2),$(3))
+  $(call _expand-inherited-values,$(1),$(2),$(3),$(4))
 
   $(eval $(1).$(2).inherited :=)
   $(eval _include_stack := $(wordlist 2,9999,$$(_include_stack)))
@@ -215,6 +223,7 @@
 # $(1): context prefix
 # $(2): list of makefiles representing nodes to import
 # $(3): list of node variable names
+# $(4): list of single value variable names (subset of $(3))
 #
 #TODO: Make the "does not exist" message more helpful;
 #      should print out the name of the file trying to include it.
@@ -225,7 +234,7 @@
         $(eval ### "skipping already-imported $(_in)") \
        , \
         $(eval $(1).$(_in).seen := true) \
-        $(call _import-node,$(1),$(strip $(_in)),$(3)) \
+        $(call _import-node,$(1),$(strip $(_in)),$(3),$(4)) \
        ) \
      , \
       $(error $(1): "$(_in)" does not exist) \
@@ -237,6 +246,8 @@
 # $(1): output list variable name, like "PRODUCTS" or "DEVICES"
 # $(2): list of makefiles representing nodes to import
 # $(3): list of node variable names
+# $(4): list with subset of variable names that take only a single value, instead
+#       of the default list semantics
 #
 define import-nodes
 $(if \
@@ -245,7 +256,7 @@
     $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \
                 should be empty here: $(_include_stack))),) \
     $(eval _include_stack := ) \
-    $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \
+    $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3),$(4)) \
     $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \
     $(eval _node_import_context :=) \
     $(eval $(1) := $($(1)) $(_in)) \
diff --git a/core/product.mk b/core/product.mk
index 66570e4..e5df5cd 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -106,30 +106,37 @@
 $(call get-product-makefiles,$(_find-android-products-files))
 endef
 
-_product_var_list :=
-_product_var_list += PRODUCT_NAME
-_product_var_list += PRODUCT_MODEL
+# Variables that are meant to hold only a single value.
+# - The value set in the current makefile takes precedence over inherited values
+# - If multiple inherited makefiles set the var, the first-inherited value wins
+_product_single_value_vars :=
+
+# Variables that are lists of values.
+_product_list_vars :=
+
+_product_single_value_vars += PRODUCT_NAME
+_product_single_value_vars += PRODUCT_MODEL
 
 # The resoure configuration options to use for this product.
-_product_var_list += PRODUCT_LOCALES
-_product_var_list += PRODUCT_AAPT_CONFIG
-_product_var_list += PRODUCT_AAPT_PREF_CONFIG
-_product_var_list += PRODUCT_AAPT_PREBUILT_DPI
-_product_var_list += PRODUCT_HOST_PACKAGES
-_product_var_list += PRODUCT_PACKAGES
-_product_var_list += PRODUCT_PACKAGES_DEBUG
-_product_var_list += PRODUCT_PACKAGES_DEBUG_ASAN
-_product_var_list += PRODUCT_PACKAGES_ENG
-_product_var_list += PRODUCT_PACKAGES_TESTS
+_product_list_vars += PRODUCT_LOCALES
+_product_list_vars += PRODUCT_AAPT_CONFIG
+_product_list_vars += PRODUCT_AAPT_PREF_CONFIG
+_product_list_vars += PRODUCT_AAPT_PREBUILT_DPI
+_product_list_vars += PRODUCT_HOST_PACKAGES
+_product_list_vars += PRODUCT_PACKAGES
+_product_list_vars += PRODUCT_PACKAGES_DEBUG
+_product_list_vars += PRODUCT_PACKAGES_DEBUG_ASAN
+_product_list_vars += PRODUCT_PACKAGES_ENG
+_product_list_vars += PRODUCT_PACKAGES_TESTS
 
 # The device that this product maps to.
-_product_var_list += PRODUCT_DEVICE
-_product_var_list += PRODUCT_MANUFACTURER
-_product_var_list += PRODUCT_BRAND
+_product_single_value_vars += PRODUCT_DEVICE
+_product_single_value_vars += PRODUCT_MANUFACTURER
+_product_single_value_vars += PRODUCT_BRAND
 
 # These PRODUCT_SYSTEM_* flags, if defined, are used in place of the
 # corresponding PRODUCT_* flags for the sysprops on /system.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_SYSTEM_NAME \
     PRODUCT_SYSTEM_MODEL \
     PRODUCT_SYSTEM_DEVICE \
@@ -138,24 +145,24 @@
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
-_product_var_list += PRODUCT_PROPERTY_OVERRIDES
+_product_list_vars += PRODUCT_PROPERTY_OVERRIDES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to default.prop
-_product_var_list += PRODUCT_DEFAULT_PROPERTY_OVERRIDES
+_product_list_vars += PRODUCT_DEFAULT_PROPERTY_OVERRIDES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to build.prop of product partition
-_product_var_list += PRODUCT_PRODUCT_PROPERTIES
+_product_list_vars += PRODUCT_PRODUCT_PROPERTIES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to build.prop of product partition
-_product_var_list += PRODUCT_PRODUCT_SERVICES_PROPERTIES
-_product_var_list += PRODUCT_ODM_PROPERTIES
-_product_var_list += PRODUCT_CHARACTERISTICS
+_product_list_vars += PRODUCT_PRODUCT_SERVICES_PROPERTIES
+_product_list_vars += PRODUCT_ODM_PROPERTIES
+_product_list_vars += PRODUCT_CHARACTERISTICS
 
 # A list of words like <source path>:<destination path>[:<owner>].
 # The file at the source path should be copied to the destination path
@@ -163,157 +170,160 @@
 # $(PRODUCT_OUT), so it should look like, e.g., "system/etc/file.xml".
 # The rules for these copy steps are defined in build/make/core/Makefile.
 # The optional :<owner> is used to indicate the owner of a vendor file.
-_product_var_list += PRODUCT_COPY_FILES
+_product_list_vars += PRODUCT_COPY_FILES
 
 # The OTA key(s) specified by the product config, if any.  The names
 # of these keys are stored in the target-files zip so that post-build
 # signing tools can substitute them for the test key embedded by
 # default.
-_product_var_list += PRODUCT_OTA_PUBLIC_KEYS
-_product_var_list += PRODUCT_EXTRA_RECOVERY_KEYS
+_product_list_vars += PRODUCT_OTA_PUBLIC_KEYS
+_product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS
 
 # Should we use the default resources or add any product specific overlays
-_product_var_list += PRODUCT_PACKAGE_OVERLAYS
-_product_var_list += DEVICE_PACKAGE_OVERLAYS
+_product_list_vars += PRODUCT_PACKAGE_OVERLAYS
+_product_list_vars += DEVICE_PACKAGE_OVERLAYS
 
 # Resource overlay list which must be excluded from enforcing RRO.
-_product_var_list += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS
+_product_list_vars += PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS
 
 # Package list to apply enforcing RRO.
-_product_var_list += PRODUCT_ENFORCE_RRO_TARGETS
+_product_list_vars += PRODUCT_ENFORCE_RRO_TARGETS
 
-_product_var_list += PRODUCT_SDK_ATREE_FILES
-_product_var_list += PRODUCT_SDK_ADDON_NAME
-_product_var_list += PRODUCT_SDK_ADDON_COPY_FILES
-_product_var_list += PRODUCT_SDK_ADDON_COPY_MODULES
-_product_var_list += PRODUCT_SDK_ADDON_DOC_MODULES
-_product_var_list += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP
+_product_list_vars += PRODUCT_SDK_ATREE_FILES
+_product_list_vars += PRODUCT_SDK_ADDON_NAME
+_product_list_vars += PRODUCT_SDK_ADDON_COPY_FILES
+_product_list_vars += PRODUCT_SDK_ADDON_COPY_MODULES
+_product_list_vars += PRODUCT_SDK_ADDON_DOC_MODULES
+_product_list_vars += PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP
 
 # which Soong namespaces to export to Make
-_product_var_list += PRODUCT_SOONG_NAMESPACES
+_product_list_vars += PRODUCT_SOONG_NAMESPACES
 
-_product_var_list += PRODUCT_DEFAULT_WIFI_CHANNELS
-_product_var_list += PRODUCT_DEFAULT_DEV_CERTIFICATE
-_product_var_list += PRODUCT_RESTRICT_VENDOR_FILES
+_product_list_vars += PRODUCT_DEFAULT_WIFI_CHANNELS
+_product_list_vars += PRODUCT_DEFAULT_DEV_CERTIFICATE
+_product_list_vars += PRODUCT_RESTRICT_VENDOR_FILES
 
 # The list of product-specific kernel header dirs
-_product_var_list += PRODUCT_VENDOR_KERNEL_HEADERS
+_product_list_vars += PRODUCT_VENDOR_KERNEL_HEADERS
 
 # A list of module names of BOOTCLASSPATH (jar files)
-_product_var_list += PRODUCT_BOOT_JARS
-_product_var_list += PRODUCT_SUPPORTS_BOOT_SIGNER
-_product_var_list += PRODUCT_SUPPORTS_VBOOT
-_product_var_list += PRODUCT_SUPPORTS_VERITY
-_product_var_list += PRODUCT_SUPPORTS_VERITY_FEC
-_product_var_list += PRODUCT_OEM_PROPERTIES
+_product_list_vars += PRODUCT_BOOT_JARS
+_product_list_vars += PRODUCT_SUPPORTS_BOOT_SIGNER
+_product_list_vars += PRODUCT_SUPPORTS_VBOOT
+_product_list_vars += PRODUCT_SUPPORTS_VERITY
+_product_list_vars += PRODUCT_SUPPORTS_VERITY_FEC
+_product_list_vars += PRODUCT_OEM_PROPERTIES
 
 # A list of property assignments, like "key = value", with zero or more
 # whitespace characters on either side of the '='.
 # used for adding properties to default.prop of system partition
-_product_var_list += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
+_product_list_vars += PRODUCT_SYSTEM_DEFAULT_PROPERTIES
 
-_product_var_list += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
-_product_var_list += PRODUCT_VENDOR_PROPERTY_BLACKLIST
-_product_var_list += PRODUCT_SYSTEM_SERVER_APPS
-_product_var_list += PRODUCT_SYSTEM_SERVER_JARS
+_product_list_vars += PRODUCT_SYSTEM_PROPERTY_BLACKLIST
+_product_list_vars += PRODUCT_VENDOR_PROPERTY_BLACKLIST
+_product_list_vars += PRODUCT_SYSTEM_SERVER_APPS
+_product_list_vars += PRODUCT_SYSTEM_SERVER_JARS
 
 # All of the apps that we force preopt, this overrides WITH_DEXPREOPT.
-_product_var_list += PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK
-_product_var_list += PRODUCT_DEXPREOPT_SPEED_APPS
-_product_var_list += PRODUCT_LOADED_BY_PRIVILEGED_MODULES
-_product_var_list += PRODUCT_VBOOT_SIGNING_KEY
-_product_var_list += PRODUCT_VBOOT_SIGNING_SUBKEY
-_product_var_list += PRODUCT_VERITY_SIGNING_KEY
-_product_var_list += PRODUCT_SYSTEM_VERITY_PARTITION
-_product_var_list += PRODUCT_VENDOR_VERITY_PARTITION
-_product_var_list += PRODUCT_PRODUCT_VERITY_PARTITION
-_product_var_list += PRODUCT_PRODUCT_SERVICES_VERITY_PARTITION
-_product_var_list += PRODUCT_ODM_VERITY_PARTITION
-_product_var_list += PRODUCT_SYSTEM_SERVER_DEBUG_INFO
-_product_var_list += PRODUCT_OTHER_JAVA_DEBUG_INFO
+_product_list_vars += PRODUCT_ALWAYS_PREOPT_EXTRACTED_APK
+_product_list_vars += PRODUCT_DEXPREOPT_SPEED_APPS
+_product_list_vars += PRODUCT_LOADED_BY_PRIVILEGED_MODULES
+_product_single_value_vars += PRODUCT_VBOOT_SIGNING_KEY
+_product_single_value_vars += PRODUCT_VBOOT_SIGNING_SUBKEY
+_product_single_value_vars += PRODUCT_VERITY_SIGNING_KEY
+_product_single_value_vars += PRODUCT_SYSTEM_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_VENDOR_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_PRODUCT_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_PRODUCT_SERVICES_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_ODM_VERITY_PARTITION
+_product_single_value_vars += PRODUCT_SYSTEM_SERVER_DEBUG_INFO
+_product_single_value_vars += PRODUCT_OTHER_JAVA_DEBUG_INFO
 
 # Per-module dex-preopt configs.
-_product_var_list += PRODUCT_DEX_PREOPT_MODULE_CONFIGS
-_product_var_list += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
-_product_var_list += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
-_product_var_list += PRODUCT_DEX_PREOPT_BOOT_FLAGS
-_product_var_list += PRODUCT_DEX_PREOPT_PROFILE_DIR
-_product_var_list += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES
-_product_var_list += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING
-_product_var_list += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS
+_product_list_vars += PRODUCT_DEX_PREOPT_MODULE_CONFIGS
+_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER
+_product_list_vars += PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
+_product_list_vars += PRODUCT_DEX_PREOPT_BOOT_FLAGS
+_product_list_vars += PRODUCT_DEX_PREOPT_PROFILE_DIR
+_product_list_vars += PRODUCT_DEX_PREOPT_GENERATE_DM_FILES
+_product_list_vars += PRODUCT_DEX_PREOPT_NEVER_ALLOW_STRIPPING
+_product_list_vars += PRODUCT_DEX_PREOPT_RESOLVE_STARTUP_STRINGS
 
 # Boot image options.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_USE_PROFILE_FOR_BOOT_IMAGE \
     PRODUCT_DEX_PREOPT_BOOT_IMAGE_PROFILE_LOCATION \
     PRODUCT_USES_DEFAULT_ART_CONFIG \
 
-_product_var_list += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER
+_product_list_vars += PRODUCT_SYSTEM_SERVER_COMPILER_FILTER
 # Per-module sanitizer configs
-_product_var_list += PRODUCT_SANITIZER_MODULE_CONFIGS
-_product_var_list += PRODUCT_SYSTEM_BASE_FS_PATH
-_product_var_list += PRODUCT_VENDOR_BASE_FS_PATH
-_product_var_list += PRODUCT_PRODUCT_BASE_FS_PATH
-_product_var_list += PRODUCT_PRODUCT_SERVICES_BASE_FS_PATH
-_product_var_list += PRODUCT_ODM_BASE_FS_PATH
-_product_var_list += PRODUCT_SHIPPING_API_LEVEL
-_product_var_list += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES
-_product_var_list += VENDOR_EXCEPTION_MODULES
-_product_var_list += VENDOR_EXCEPTION_PATHS
+_product_list_vars += PRODUCT_SANITIZER_MODULE_CONFIGS
+_product_single_value_vars += PRODUCT_SYSTEM_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_VENDOR_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_PRODUCT_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_PRODUCT_SERVICES_BASE_FS_PATH
+_product_single_value_vars += PRODUCT_ODM_BASE_FS_PATH
 
+# The first API level this product shipped with
+_product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL
+
+_product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES
+_product_list_vars += VENDOR_EXCEPTION_MODULES
+_product_list_vars += VENDOR_EXCEPTION_PATHS
 # Whether the product wants to ship libartd. For rules and meaning, see art/Android.mk.
-_product_var_list += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD
+_product_single_value_vars += PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD
 
 # Make this art variable visible to soong_config.mk.
-_product_var_list += PRODUCT_ART_USE_READ_BARRIER
+_product_single_value_vars += PRODUCT_ART_USE_READ_BARRIER
 
 # Whether the product is an Android Things variant.
-_product_var_list += PRODUCT_IOT
+_product_single_value_vars += PRODUCT_IOT
 
 # Add reserved headroom to a system image.
-_product_var_list += PRODUCT_SYSTEM_HEADROOM
+_product_single_value_vars += PRODUCT_SYSTEM_HEADROOM
 
 # Whether to save disk space by minimizing java debug info
-_product_var_list += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO
+_product_single_value_vars += PRODUCT_MINIMIZE_JAVA_DEBUG_INFO
 
 # Whether any paths are excluded from sanitization when SANITIZE_TARGET=integer_overflow
-_product_var_list += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_INTEGER_OVERFLOW_EXCLUDE_PATHS
 
-_product_var_list += PRODUCT_ADB_KEYS
+_product_single_value_vars += PRODUCT_ADB_KEYS
 
 # Whether any paths should have CFI enabled for components
-_product_var_list += PRODUCT_CFI_INCLUDE_PATHS
+_product_list_vars += PRODUCT_CFI_INCLUDE_PATHS
 
 # Whether any paths are excluded from sanitization when SANITIZE_TARGET=cfi
-_product_var_list += PRODUCT_CFI_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_CFI_EXCLUDE_PATHS
 
 # Whether the Scudo hardened allocator is disabled platform-wide
-_product_var_list += PRODUCT_DISABLE_SCUDO
+_product_single_value_vars += PRODUCT_DISABLE_SCUDO
 
 # A flag to override PRODUCT_COMPATIBLE_PROPERTY
-_product_var_list += PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE
+_product_single_value_vars += PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE
 
 # Whether the whitelist of actionable compatible properties should be disabled or not
-_product_var_list += PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE
-_product_var_list += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
-_product_var_list += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT
-_product_var_list += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST
-_product_var_list += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT
-_product_var_list += PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST
+_product_single_value_vars += PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE
+
+_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
+_product_single_value_vars += PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT
+_product_list_vars += PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_WHITELIST
+_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_HINT
+_product_list_vars += PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST
 
 # List of modules that should be forcefully unmarked from being LOCAL_PRODUCT_MODULE, and hence
 # installed on /system directory by default.
-_product_var_list += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION
+_product_list_vars += PRODUCT_FORCE_PRODUCT_MODULES_TO_SYSTEM_PARTITION
 
 # When this is true, dynamic partitions is retrofitted on a device that has
 # already been launched without dynamic partitions. Otherwise, the device
 # is launched with dynamic partitions.
 # This flag implies PRODUCT_USE_DYNAMIC_PARTITIONS.
-_product_var_list += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS
+_product_single_value_vars += PRODUCT_RETROFIT_DYNAMIC_PARTITIONS
 
 # Other dynamic partition feature flags.PRODUCT_USE_DYNAMIC_PARTITION_SIZE and
 # PRODUCT_BUILD_SUPER_PARTITION default to the value of PRODUCT_USE_DYNAMIC_PARTITIONS.
-_product_var_list += \
+_product_single_value_vars += \
     PRODUCT_USE_DYNAMIC_PARTITIONS \
     PRODUCT_USE_DYNAMIC_PARTITION_SIZE \
     PRODUCT_BUILD_SUPER_PARTITION \
@@ -322,33 +332,37 @@
 # during OTA). Otherwise, kernel configuration requirements are enforced in VTS.
 # Devices that checks the running kernel (instead of the kernel in OTA package) should not
 # set this variable to prevent OTA failures.
-_product_var_list += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
+_product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
 
 # If set to true, this product builds a generic OTA package, which installs generic system images
 # onto matching devices. The product may only build a subset of system images (e.g. only
 # system.img), so devices need to install the package in a system-only OTA manner.
-_product_var_list += PRODUCT_BUILD_GENERIC_OTA_PACKAGE
+_product_single_value_vars += PRODUCT_BUILD_GENERIC_OTA_PACKAGE
 
 # Whether any paths are excluded from being set XOM when ENABLE_XOM=true
-_product_var_list += PRODUCT_XOM_EXCLUDE_PATHS
-_product_var_list += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
-_product_var_list += PRODUCT_PACKAGE_NAME_OVERRIDES
-_product_var_list += PRODUCT_CERTIFICATE_OVERRIDES
-_product_var_list += PRODUCT_BUILD_SYSTEM_IMAGE
-_product_var_list += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
-_product_var_list += PRODUCT_BUILD_VENDOR_IMAGE
-_product_var_list += PRODUCT_BUILD_PRODUCT_IMAGE
-_product_var_list += PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE
-_product_var_list += PRODUCT_BUILD_ODM_IMAGE
-_product_var_list += PRODUCT_BUILD_CACHE_IMAGE
-_product_var_list += PRODUCT_BUILD_RAMDISK_IMAGE
-_product_var_list += PRODUCT_BUILD_USERDATA_IMAGE
-_product_var_list += PRODUCT_UPDATABLE_BOOT_MODULES
-_product_var_list += PRODUCT_UPDATABLE_BOOT_LOCATIONS
+_product_list_vars += PRODUCT_XOM_EXCLUDE_PATHS
+_product_list_vars += PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+_product_list_vars += PRODUCT_PACKAGE_NAME_OVERRIDES
+_product_list_vars += PRODUCT_CERTIFICATE_OVERRIDES
 
+# Controls for whether different partitions are built for the current product.
+_product_single_value_vars += PRODUCT_BUILD_SYSTEM_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_SYSTEM_OTHER_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_VENDOR_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PRODUCT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_ODM_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_CACHE_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_RAMDISK_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE
+
+_product_list_vars += PRODUCT_UPDATABLE_BOOT_MODULES
+_product_list_vars += PRODUCT_UPDATABLE_BOOT_LOCATIONS
 # Whether the product would like to check prebuilt ELF files.
-_product_var_list += PRODUCT_CHECK_ELF_FILES
-.KATI_READONLY := _product_var_list
+_product_single_value_vars += PRODUCT_CHECK_ELF_FILES
+
+.KATI_READONLY := _product_single_value_vars _product_list_vars
+_product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
 
 define dump-product
 $(warning ==== $(1) ====)\
@@ -422,7 +436,7 @@
 #
 #TODO: check to make sure that products have all the necessary vars defined
 define import-products
-$(call import-nodes,PRODUCTS,$(1),$(_product_var_list))
+$(call import-nodes,PRODUCTS,$(1),$(_product_var_list),$(_product_single_value_vars))
 endef
 
 
diff --git a/core/product_config.mk b/core/product_config.mk
index a088f06..12c2722 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -390,7 +390,6 @@
 
 # Macro to use below. $(1) is the name of the partition
 define product-build-image-config
-PRODUCT_BUILD_$(1)_IMAGE := $$(firstword $$(PRODUCT_BUILD_$(1)_IMAGE))
 ifneq ($$(filter-out true false,$$(PRODUCT_BUILD_$(1)_IMAGE)),)
     $$(error Invalid PRODUCT_BUILD_$(1)_IMAGE: $$(PRODUCT_BUILD_$(1)_IMAGE) -- true false and empty are supported)
 endif
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 689e095..20a9c64 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -160,6 +160,7 @@
 
 default_other_item_list = [
     'META/boot_filesystem_config.txt',
+    'META/file_contexts.bin',
     'META/otakeys.txt',
     'META/releasetools.py',
     'META/vendor_filesystem_config.txt',
@@ -371,6 +372,63 @@
             'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
 
 
+def merge_dynamic_partition_info_dicts(system_dict,
+                                       other_dict,
+                                       include_dynamic_partition_list=True,
+                                       size_prefix='',
+                                       size_suffix='',
+                                       list_prefix='',
+                                       list_suffix=''):
+  """Merges dynamic partition info variables.
+
+  Args:
+    system_dict: The dictionary of dynamic partition info variables from the
+      partial system target files.
+    other_dict: The dictionary of dynamic partition info variables from the
+      partial other target files.
+    include_dynamic_partition_list: If true, merges the dynamic_partition_list
+      variable. Not all use cases need this variable merged.
+    size_prefix: The prefix in partition group size variables that precedes the
+      name of the partition group. For example, partition group 'group_a' with
+      corresponding size variable 'super_group_a_group_size' would have the
+      size_prefix 'super_'.
+    size_suffix: Similar to size_prefix but for the variable's suffix. For
+      example, 'super_group_a_group_size' would have size_suffix '_group_size'.
+    list_prefix: Similar to size_prefix but for the partition group's
+      partition_list variable.
+    list_suffix: Similar to size_suffix but for the partition group's
+      partition_list variable.
+
+  Returns:
+    The merged dynamic partition info dictionary.
+  """
+  merged_dict = {}
+  # Partition groups and group sizes are defined by the other (non-system)
+  # dict because these values may vary for each board that uses a shared system
+  # image.
+  merged_dict['super_partition_groups'] = other_dict['super_partition_groups']
+  if include_dynamic_partition_list:
+    system_dynamic_partition_list = system_dict.get('dynamic_partition_list',
+                                                    '')
+    other_dynamic_partition_list = other_dict.get('dynamic_partition_list', '')
+    merged_dict['dynamic_partition_list'] = (
+        '%s %s' %
+        (system_dynamic_partition_list, other_dynamic_partition_list)).strip()
+  for partition_group in merged_dict['super_partition_groups'].split(' '):
+    # Set the partition group's size using the value from the other dict.
+    key = '%s%s%s' % (size_prefix, partition_group, size_suffix)
+    if key not in other_dict:
+      raise ValueError('Other dict does not contain required key %s.' % key)
+    merged_dict[key] = other_dict[key]
+
+    # Set the partition group's partition list using a concatenation of the
+    # system and other partition lists.
+    key = '%s%s%s' % (list_prefix, partition_group, list_suffix)
+    merged_dict[key] = (
+        '%s %s' % (system_dict.get(key, ''), other_dict.get(key, ''))).strip()
+  return merged_dict
+
+
 def process_misc_info_txt(system_target_files_temp_dir,
                           other_target_files_temp_dir,
                           output_target_files_temp_dir, system_misc_info_keys):
@@ -416,107 +474,77 @@
   # Merge misc info keys used for Dynamic Partitions.
   if (merged_info_dict.get('use_dynamic_partitions') == 'true') and (
       system_info_dict.get('use_dynamic_partitions') == 'true'):
-    merged_info_dict['dynamic_partition_list'] = '%s %s' % (
-        system_info_dict.get('dynamic_partition_list', ''),
-        merged_info_dict.get('dynamic_partition_list', ''))
-    # Partition groups and group sizes are defined by the other (non-system)
-    # misc info file because these values may vary for each board that uses
-    # a shared system image.
-    for partition_group in merged_info_dict['super_partition_groups'].split(
-        ' '):
-      if ('super_%s_group_size' % partition_group) not in merged_info_dict:
-        raise ValueError(
-            'Other META/misc_info.txt does not contain required key '
-            'super_%s_group_size.' % partition_group)
-      key = 'super_%s_partition_list' % partition_group
-      merged_info_dict[key] = '%s %s' % (system_info_dict.get(
-          key, ''), merged_info_dict.get(key, ''))
+    merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
+        system_dict=system_info_dict,
+        other_dict=merged_info_dict,
+        size_prefix='super_',
+        size_suffix='_group_size',
+        list_prefix='super_',
+        list_suffix='_partition_list')
+    merged_info_dict.update(merged_dynamic_partitions_dict)
 
   output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
                                       'misc_info.txt')
-
-  sorted_keys = sorted(merged_info_dict.keys())
-
   with open(output_misc_info_txt, 'w') as output:
+    sorted_keys = sorted(merged_info_dict.keys())
     for key in sorted_keys:
       output.write('{}={}\n'.format(key, merged_info_dict[key]))
 
 
-def process_file_contexts_bin(temp_dir, output_target_files_temp_dir):
-  """Perform special processing for META/file_contexts.bin.
+def process_dynamic_partitions_info_txt(system_target_files_dir,
+                                        other_target_files_dir,
+                                        output_target_files_dir):
+  """Perform special processing for META/dynamic_partitions_info.txt.
 
-  This function combines plat_file_contexts and vendor_file_contexts, which are
-  expected to already be extracted in temp_dir, to produce a merged
-  file_contexts.bin that will land in temp_dir at META/file_contexts.bin.
+  This function merges the contents of the META/dynamic_partitions_info.txt
+  files from the system directory and the other directory, placing the merged
+  result in the output directory.
+
+  This function does nothing if META/dynamic_partitions_info.txt from the other
+  directory does not exist.
 
   Args:
-    temp_dir: The name of a scratch directory that this function can use for
-      intermediate files generated during processing.
-    output_target_files_temp_dir: The name of the working directory that must
-      already contain plat_file_contexts and vendor_file_contexts (in the
-      appropriate sub directories), and to which META/file_contexts.bin will be
-      written.
+    system_target_files_dir: The name of a directory containing the special
+      items extracted from the system target files package.
+    other_target_files_dir: The name of a directory containing the special items
+      extracted from the other target files package.
+    output_target_files_dir: The name of a directory that will be used to create
+      the output target files package after all the special cases are processed.
   """
 
-  # To create a merged file_contexts.bin file, we use the system and vendor
-  # file contexts files as input, the m4 tool to combine them, the sorting tool
-  # to sort, and finally the sefcontext_compile tool to generate the final
-  # output. We currently omit a checkfc step since the files had been checked
-  # as part of the build.
+  if not os.path.exists(
+      os.path.join(other_target_files_dir, 'META',
+                   'dynamic_partitions_info.txt')):
+    return
 
-  # The m4 step concatenates the two input files contexts files. Since m4
-  # writes to stdout, we receive that into an array of bytes, and then write it
-  # to a file.
+  def read_helper(d):
+    dynamic_partitions_info_txt = os.path.join(d, 'META',
+                                               'dynamic_partitions_info.txt')
+    with open(dynamic_partitions_info_txt) as f:
+      return list(f.read().splitlines())
 
-  # Collect the file contexts that we're going to combine from SYSTEM, VENDOR,
-  # PRODUCT, and ODM. We require SYSTEM and VENDOR, but others are optional.
+  system_dynamic_partitions_dict = common.LoadDictionaryFromLines(
+      read_helper(system_target_files_dir))
+  other_dynamic_partitions_dict = common.LoadDictionaryFromLines(
+      read_helper(other_target_files_dir))
 
-  file_contexts_list = []
+  merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
+      system_dict=system_dynamic_partitions_dict,
+      other_dict=other_dynamic_partitions_dict,
+      # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
+      include_dynamic_partition_list=False,
+      size_suffix='_size',
+      list_suffix='_partition_list')
 
-  for partition in ['SYSTEM', 'VENDOR', 'PRODUCT', 'ODM']:
-    prefix = 'plat' if partition == 'SYSTEM' else partition.lower()
-
-    file_contexts = os.path.join(output_target_files_temp_dir, partition, 'etc',
-                                 'selinux', prefix + '_file_contexts')
-
-    mandatory = partition in ['SYSTEM', 'VENDOR']
-
-    if mandatory or os.path.isfile(file_contexts):
-      file_contexts_list.append(file_contexts)
-    else:
-      logger.warning('file not found: %s', file_contexts)
-
-  command = ['m4', '--fatal-warnings', '-s'] + file_contexts_list
-
-  merged_content = common.RunAndCheckOutput(command, verbose=False)
-
-  merged_file_contexts_txt = os.path.join(temp_dir, 'merged_file_contexts.txt')
-
-  with open(merged_file_contexts_txt, 'wb') as f:
-    f.write(merged_content)
-
-  # The sort step sorts the concatenated file.
-
-  sorted_file_contexts_txt = os.path.join(temp_dir, 'sorted_file_contexts.txt')
-  command = ['fc_sort', merged_file_contexts_txt, sorted_file_contexts_txt]
-  common.RunAndWait(command, verbose=True)
-
-  # Finally, the compile step creates the final META/file_contexts.bin.
-
-  file_contexts_bin = os.path.join(output_target_files_temp_dir, 'META',
-                                   'file_contexts.bin')
-
-  command = [
-      'sefcontext_compile',
-      '-o',
-      file_contexts_bin,
-      sorted_file_contexts_txt,
-  ]
-
-  common.RunAndWait(command, verbose=True)
+  output_dynamic_partitions_info_txt = os.path.join(
+      output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
+  with open(output_dynamic_partitions_info_txt, 'w') as output:
+    sorted_keys = sorted(merged_dynamic_partitions_dict.keys())
+    for key in sorted_keys:
+      output.write('{}={}\n'.format(key, merged_dynamic_partitions_dict[key]))
 
 
-def process_special_cases(temp_dir, system_target_files_temp_dir,
+def process_special_cases(system_target_files_temp_dir,
                           other_target_files_temp_dir,
                           output_target_files_temp_dir, system_misc_info_keys,
                           rebuild_recovery):
@@ -526,8 +554,6 @@
   processing. This function performs all that special-case processing.
 
   Args:
-    temp_dir: The name of a scratch directory that this function can use for
-      intermediate files generated during processing.
     system_target_files_temp_dir: The name of a directory containing the special
       items extracted from the system target files package.
     other_target_files_temp_dir: The name of a directory containing the special
@@ -557,8 +583,9 @@
       output_target_files_temp_dir=output_target_files_temp_dir,
       system_misc_info_keys=system_misc_info_keys)
 
-  process_file_contexts_bin(
-      temp_dir=temp_dir,
+  process_dynamic_partitions_info_txt(
+      system_target_files_temp_dir=system_target_files_temp_dir,
+      other_target_files_temp_dir=other_target_files_temp_dir,
       output_target_files_temp_dir=output_target_files_temp_dir)
 
 
@@ -655,7 +682,6 @@
   # files package are in place.
 
   process_special_cases(
-      temp_dir=temp_dir,
       system_target_files_temp_dir=system_target_files_temp_dir,
       other_target_files_temp_dir=other_target_files_temp_dir,
       output_target_files_temp_dir=output_target_files_temp_dir,
@@ -728,7 +754,8 @@
       output_target_files_meta_dir,
   ]
   find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
-  meta_content = common.RunAndCheckOutput(['sort'], stdin=find_process.stdout,
+  meta_content = common.RunAndCheckOutput(['sort'],
+                                          stdin=find_process.stdout,
                                           verbose=False)
 
   find_command = [
@@ -736,7 +763,8 @@
       output_target_files_meta_dir, '-prune', '-o', '-print'
   ]
   find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
-  other_content = common.RunAndCheckOutput(['sort'], stdin=find_process.stdout,
+  other_content = common.RunAndCheckOutput(['sort'],
+                                           stdin=find_process.stdout,
                                            verbose=False)
 
   with open(output_target_files_list, 'wb') as f:
@@ -766,7 +794,6 @@
     ota_from_target_files.main(ota_from_target_files_args)
 
 
-
 def call_func_with_temp_dir(func, keep_tmp):
   """Manage the creation and cleanup of the temporary directory.
 
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index d25ae0c..24ee91b 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -91,12 +91,15 @@
       Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
       with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
 
-  --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_algorithm <algorithm>
-  --avb_{boot,system,system_other,vendor,dtbo,vbmeta}_key <key>
+  --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_algorithm <algorithm>
+  --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_key <key>
       Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
       the specified image. Otherwise it uses the existing values in info dict.
 
-  --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta}_extra_args <args>
+  --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
+         vbmeta_vendor}_extra_args <args>
       Specify any additional args that are needed to AVB-sign the image
       (e.g. "--signing_helper /path/to/helper"). The args will be appended to
       the existing ones in info dict.
@@ -966,6 +969,8 @@
       'system_other' : 'avb_system_other_add_hashtree_footer_args',
       'vendor' : 'avb_vendor_add_hashtree_footer_args',
       'vbmeta' : 'avb_vbmeta_args',
+      'vbmeta_system' : 'avb_vbmeta_system_args',
+      'vbmeta_vendor' : 'avb_vbmeta_vendor_args',
   }
 
   def ReplaceAvbPartitionSigningKey(partition):
@@ -1193,6 +1198,18 @@
       OPTIONS.avb_algorithms['vendor'] = a
     elif o == "--avb_vendor_extra_args":
       OPTIONS.avb_extra_args['vendor'] = a
+    elif o == "--avb_vbmeta_system_key":
+      OPTIONS.avb_keys['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_system_algorithm":
+      OPTIONS.avb_algorithms['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_system_extra_args":
+      OPTIONS.avb_extra_args['vbmeta_system'] = a
+    elif o == "--avb_vbmeta_vendor_key":
+      OPTIONS.avb_keys['vbmeta_vendor'] = a
+    elif o == "--avb_vbmeta_vendor_algorithm":
+      OPTIONS.avb_algorithms['vbmeta_vendor'] = a
+    elif o == "--avb_vbmeta_vendor_extra_args":
+      OPTIONS.avb_extra_args['vbmeta_vendor'] = a
     elif o == "--avb_apex_extra_args":
       OPTIONS.avb_extra_args['apex'] = a
     else:
@@ -1232,6 +1249,12 @@
           "avb_vendor_algorithm=",
           "avb_vendor_key=",
           "avb_vendor_extra_args=",
+          "avb_vbmeta_system_algorithm=",
+          "avb_vbmeta_system_key=",
+          "avb_vbmeta_system_extra_args=",
+          "avb_vbmeta_vendor_algorithm=",
+          "avb_vbmeta_vendor_key=",
+          "avb_vbmeta_vendor_extra_args=",
       ],
       extra_option_handler=option_handler)
 
diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py
index 7e18a34..3f15d8f 100644
--- a/tools/releasetools/test_merge_target_files.py
+++ b/tools/releasetools/test_merge_target_files.py
@@ -21,7 +21,8 @@
 from merge_target_files import (read_config_list, validate_config_lists,
                                 default_system_item_list,
                                 default_other_item_list,
-                                default_system_misc_info_keys, copy_items)
+                                default_system_misc_info_keys, copy_items,
+                                merge_dynamic_partition_info_dicts)
 
 
 class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
@@ -128,3 +129,34 @@
       self.assertFalse(
           validate_config_lists(default_system_item_list, system_misc_info_keys,
                                 default_other_item_list))
+
+  def test_merge_dynamic_partition_info_dicts_ReturnsMergedDict(self):
+    system_dict = {
+        'super_partition_groups': 'group_a',
+        'dynamic_partition_list': 'system',
+        'super_group_a_list': 'system',
+    }
+    other_dict = {
+        'super_partition_groups': 'group_a group_b',
+        'dynamic_partition_list': 'vendor product',
+        'super_group_a_list': 'vendor',
+        'super_group_a_size': '1000',
+        'super_group_b_list': 'product',
+        'super_group_b_size': '2000',
+    }
+    merged_dict = merge_dynamic_partition_info_dicts(
+        system_dict=system_dict,
+        other_dict=other_dict,
+        size_prefix='super_',
+        size_suffix='_size',
+        list_prefix='super_',
+        list_suffix='_list')
+    expected_merged_dict = {
+        'super_partition_groups': 'group_a group_b',
+        'dynamic_partition_list': 'system vendor product',
+        'super_group_a_list': 'system vendor',
+        'super_group_a_size': '1000',
+        'super_group_b_list': 'product',
+        'super_group_b_size': '2000',
+    }
+    self.assertEqual(merged_dict, expected_merged_dict)