Merge "Add virtual A/B product."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 426e25d..a84e793 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -646,6 +646,15 @@
 # link_type and jni_link_type files are no longer needed
 $(call add-clean-step, find $(OUT_DIR) -type f -name "*link_type" -print0 | xargs -0 rm -f)
 
+# import_includes and export_includes files are no longer needed
+$(call add-clean-step, find $(OUT_DIR) -type f -name "import_includes" -o -name "export_includes" -print0 | xargs -0 rm -f)
+
+# Recreate product and system_ext partitions for emulator
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*product*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*system_ext*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/product)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/generic*/*/system_ext)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/common/core.mk b/common/core.mk
index e5264b0..7d505c0 100644
--- a/common/core.mk
+++ b/common/core.mk
@@ -42,6 +42,9 @@
 backslash := \a
 backslash := $(patsubst %a,%,$(backslash))
 
+TOP :=$= .
+TOPDIR :=$=
+
 # Prevent accidentally changing these variables
 .KATI_READONLY := SHELL empty space comma newline pound backslash
 
diff --git a/core/Makefile b/core/Makefile
index 40c374f..6ced027 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3434,8 +3434,7 @@
 # (1): list of items like "system", "vendor", "product", "system_ext"
 # return: map each item into a command ( wrapped in $$() ) that reads the size
 define read-size-of-partitions
-$(foreach image,$(call images-for-partitions,$(1)),$$( \
-    build/make/tools/releasetools/sparse_img.py --get_partition_size $(image)))
+$(foreach image,$(call images-for-partitions,$(1)),$$($(SPARSE_IMG) --get_partition_size $(image)))
 endef
 
 # round result to BOARD_SUPER_PARTITION_ALIGNMENT
@@ -3472,7 +3471,7 @@
 
 # Add image dependencies so that generated_*_image_info.txt are written before checking.
 $(check_all_partition_sizes_file): \
-    build/make/tools/releasetools/sparse_img.py \
+    $(SPARSE_IMG) \
     $(call images-for-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST))
 
 ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
@@ -3658,6 +3657,7 @@
   mksquashfs \
   mksquashfsimage.sh \
   mkuserimg_mke2fs \
+  ota_from_target_files \
   sefcontext_compile \
   sgdisk \
   shflags \
@@ -4017,9 +4017,8 @@
 	    $(SOONG_APEX_KEYS_FILE) \
 	    $(SOONG_ZIP) \
 	    $(HOST_OUT_EXECUTABLES)/fs_config \
-	    $(HOST_OUT_EXECUTABLES)/imgdiff \
-	    $(HOST_OUT_EXECUTABLES)/bsdiff \
 	    $(HOST_OUT_EXECUTABLES)/care_map_generator \
+	    $(MAKE_RECOVERY_PATCH) \
 	    $(BUILD_IMAGE_SRCS) \
 	    $(BUILT_ASSEMBLED_FRAMEWORK_MANIFEST) \
 	    $(BUILT_ASSEMBLED_VENDOR_MANIFEST) \
@@ -4178,7 +4177,7 @@
 ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
 ifdef BUILDING_SYSTEM_IMAGE
 	$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
-	    build/make/tools/releasetools/make_recovery_patch $(zip_root) $(zip_root)
+	    $(MAKE_RECOVERY_PATCH) $(zip_root) $(zip_root)
 endif # BUILDING_SYSTEM_IMAGE
 endif
 ifeq ($(AB_OTA_UPDATER),true)
@@ -4322,13 +4321,13 @@
 # $(2): additional args
 define build-ota-package-target
 PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
-    build/make/tools/releasetools/ota_from_target_files \
-    --verbose \
-    --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
-    --path $(HOST_OUT) \
-    $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
-    $(2) \
-    $(BUILT_TARGET_FILES_PACKAGE) $(1)
+    $(OTA_FROM_TARGET_FILES) \
+        --verbose \
+        --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
+        --path $(HOST_OUT) \
+        $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
+        $(2) \
+        $(BUILT_TARGET_FILES_PACKAGE) $(1)
 endef
 
 name := $(TARGET_PRODUCT)
@@ -4338,21 +4337,11 @@
 name := $(name)-ota-$(FILE_NAME_TAG)
 
 INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
-
 INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata
 
 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
-
-ifeq ($(AB_OTA_UPDATER),true)
-$(INTERNAL_OTA_PACKAGE_TARGET): $(BRILLO_UPDATE_PAYLOAD)
-else
-$(INTERNAL_OTA_PACKAGE_TARGET): $(BROTLI)
-endif
-
 $(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA)
-
-$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) \
-	    build/make/tools/releasetools/ota_from_target_files
+$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES)
 	@echo "Package OTA: $@"
 	$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA))
 
@@ -4367,17 +4356,10 @@
 name := $(name)-ota-retrofit-$(FILE_NAME_TAG)
 
 INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
-
 $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
-
-ifeq ($(AB_OTA_UPDATER),true)
-$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BRILLO_UPDATE_PAYLOAD)
-else
-$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BROTLI)
-endif
-
-$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) \
-	    build/make/tools/releasetools/ota_from_target_files
+$(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET): \
+    $(BUILT_TARGET_FILES_PACKAGE) \
+    $(OTA_FROM_TARGET_FILES)
 	@echo "Package OTA (retrofit dynamic partitions): $@"
 	$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --retrofit_dynamic_partitions)
 
@@ -4697,6 +4679,7 @@
 
 .PHONY: updatepackage
 updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
+$(call dist-for-goals,updatepackage,$(INTERNAL_UPDATE_PACKAGE_TARGET))
 
 
 # -----------------------------------------------------------------
diff --git a/core/binary.mk b/core/binary.mk
index 874a4d3..e916164 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -119,6 +119,8 @@
     $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module)
   endif
 
+  my_cflags += -D__ANDROID_NDK__
+
   # Make sure we've built the NDK.
   my_additional_dependencies += $(SOONG_OUT_DIR)/ndk_base.timestamp
 
@@ -1174,31 +1176,6 @@
 
 
 ####################################################
-## Import includes
-####################################################
-import_includes := $(intermediates)/import_includes
-import_includes_deps := $(strip \
-    $(if $(LOCAL_USE_VNDK),\
-      $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes) \
-    $(foreach l, $(installed_shared_library_module_names), \
-      $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes) \
-    $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \
-      $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes) \
-    $(foreach l, $(my_header_libraries), \
-      $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
-$(import_includes): PRIVATE_IMPORT_EXPORT_INCLUDES := $(import_includes_deps)
-$(import_includes) : $(import_includes_deps)
-	@echo Import includes file: $@
-	$(hide) mkdir -p $(dir $@) && rm -f $@
-ifdef import_includes_deps
-	$(hide) for f in $(PRIVATE_IMPORT_EXPORT_INCLUDES); do \
-	  cat $$f >> $@; \
-	done
-else
-	$(hide) touch $@
-endif
-
-####################################################
 ## Verify that NDK-built libraries only link against
 ## other NDK-built libraries
 ####################################################
@@ -1309,7 +1286,6 @@
 # that custom build rules which generate .o files don't consume other generated
 # sources as input (or if they do they take care of that dependency themselves).
 $(normal_objects) : | $(my_generated_sources)
-$(all_objects) : $(import_includes)
 ALL_C_CPP_ETC_OBJECTS += $(all_objects)
 
 
@@ -1678,6 +1654,22 @@
     $(LOCAL_INTERMEDIATE_TARGETS): $(my_coverage_lib)
 endif
 
+####################################################
+## Import includes
+####################################################
+imported_includes := $(strip \
+    $(if $(LOCAL_USE_VNDK),\
+      $(call intermediates-dir-for,HEADER_LIBRARIES,device_kernel_headers,$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \
+    $(foreach l, $(installed_shared_library_module_names), \
+      $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \
+    $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \
+      $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))) \
+    $(foreach l, $(my_header_libraries), \
+      $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))
+
+$(foreach dep,$(imported_includes),\
+  $(eval EXPORTS.$$(dep).USERS := $$(EXPORTS.$$(dep).USERS) $$(all_objects)))
+
 ###########################################################
 ## Define PRIVATE_ variables used by multiple module types
 ###########################################################
@@ -1730,7 +1722,7 @@
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RTTI_FLAG := $(LOCAL_RTTI_FLAG)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEBUG_CFLAGS := $(debug_cflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_C_INCLUDES := $(my_c_includes)
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORT_INCLUDES := $(import_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORTED_INCLUDES := $(imported_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(my_ldflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(my_ldlibs)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_CHECKS := $(my_tidy_checks)
@@ -1757,51 +1749,30 @@
 ###########################################################
 # Export includes
 ###########################################################
-export_includes := $(intermediates)/export_includes
-export_cflags := $(foreach d,$(my_export_c_include_dirs),-I $(d))
-$(export_includes): PRIVATE_EXPORT_CFLAGS := $(export_cflags)
+
 # Headers exported by whole static libraries are also exported by this library.
 export_include_deps := $(strip \
    $(foreach l,$(my_whole_static_libraries), \
-     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))
 # Re-export requested headers from shared libraries.
 export_include_deps += $(strip \
    $(foreach l,$(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS), \
-     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))
 # Re-export requested headers from static libraries.
 export_include_deps += $(strip \
    $(foreach l,$(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS), \
-     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))
 # Re-export requested headers from header libraries.
 export_include_deps += $(strip \
    $(foreach l,$(LOCAL_EXPORT_HEADER_LIBRARY_HEADERS), \
-     $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
-$(export_includes): PRIVATE_REEXPORTED_INCLUDES := $(export_include_deps)
-# By adding $(my_generated_sources) it makes sure the headers get generated
-# before any dependent source files get compiled.
-$(export_includes) : $(my_export_c_include_deps) $(my_generated_sources) $(export_include_deps) $(LOCAL_EXPORT_C_INCLUDE_DEPS)
-	@echo Export includes file: $< -- $@
-	$(hide) mkdir -p $(dir $@) && rm -f $@.tmp && touch $@.tmp
-ifdef export_cflags
-	$(hide) echo "$(PRIVATE_EXPORT_CFLAGS)" >>$@.tmp
-endif
-ifdef export_include_deps
-	$(hide) for f in $(PRIVATE_REEXPORTED_INCLUDES); do \
-		cat $$f >> $@.tmp; \
-		done
-endif
-	$(hide) if cmp -s $@.tmp $@ ; then \
-	  rm $@.tmp ; \
-	else \
-	  mv $@.tmp $@ ; \
-	fi
-export_cflags :=
+     $(call intermediates-dir-for,HEADER_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))))
 
-# Kati adds restat=1 to ninja. GNU make does nothing for this.
-.KATI_RESTAT: $(export_includes)
-
-# Make sure export_includes gets generated when you are running mm/mmm
-$(LOCAL_BUILT_MODULE) : | $(export_includes)
+ifneq ($(strip $(my_export_c_include_dirs)$(export_include_deps)),)
+  EXPORTS_LIST := $(EXPORTS_LIST) $(intermediates)
+  EXPORTS.$(intermediates).FLAGS := $(foreach d,$(my_export_c_include_dirs),-I $(d))
+  EXPORTS.$(intermediates).REEXPORT := $(export_include_deps)
+  EXPORTS.$(intermediates).DEPS := $(my_export_c_include_deps) $(my_generated_sources) $(LOCAL_EXPORT_C_INCLUDE_DEPS)
+endif
 
 ifneq (,$(filter-out $(LOCAL_PATH)/%,$(my_export_c_include_dirs)))
 my_soong_problems += non_local__export_c_include_dirs
diff --git a/core/board_config.mk b/core/board_config.mk
index 4045c71..db60cee 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -206,6 +206,14 @@
   endif
 endif
 
+# "arm64-v8a-hwasan", the ABI for libraries compiled with HWASAN, is supported
+# in all builds with SANITIZE_TARGET=hwaddress.
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+  ifneq ($(filter arm64-v8a,$(TARGET_CPU_ABI_LIST_64_BIT)),)
+    TARGET_CPU_ABI_LIST_64_BIT := arm64-v8a-hwasan $(TARGET_CPU_ABI_LIST_64_BIT)
+  endif
+endif
+
 ifeq (,$(TARGET_CPU_ABI_LIST_32_BIT))
   ifneq (true,$(TARGET_IS_64_BIT))
     TARGET_CPU_ABI_LIST_32_BIT := $(TARGET_CPU_ABI) $(TARGET_CPU_ABI2)
diff --git a/core/cc_prebuilt_internal.mk b/core/cc_prebuilt_internal.mk
index 0c87151..a8930d5 100644
--- a/core/cc_prebuilt_internal.mk
+++ b/core/cc_prebuilt_internal.mk
@@ -75,18 +75,9 @@
   built_module := $(LOCAL_BUILT_MODULE)
 
 ifdef prebuilt_module_is_a_library
-export_includes := $(intermediates)/export_includes
-export_cflags := $(foreach d,$(LOCAL_EXPORT_C_INCLUDE_DIRS),-I $(d))
-$(export_includes): PRIVATE_EXPORT_CFLAGS := $(export_cflags)
-$(export_includes): $(LOCAL_EXPORT_C_INCLUDE_DEPS)
-	@echo Export includes file: $< -- $@
-	$(hide) mkdir -p $(dir $@) && rm -f $@
-ifdef export_cflags
-	$(hide) echo "$(PRIVATE_EXPORT_CFLAGS)" >$@
-else
-	$(hide) touch $@
-endif
-export_cflags :=
+EXPORTS_LIST := $(EXPORTS_LIST) $(intermediates)
+EXPORTS.$(intermediates).FLAGS := $(foreach d,$(LOCAL_EXPORT_C_INCLUDE_DIRS),-I $(d))
+EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS)
 
 include $(BUILD_SYSTEM)/allowed_ndk_types.mk
 
diff --git a/core/config.mk b/core/config.mk
index ee87632..0f9f112 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -576,7 +576,6 @@
 VTSC := $(HOST_OUT_EXECUTABLES)/vtsc$(HOST_EXECUTABLE_SUFFIX)
 MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
 MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX)
-BROTLI := $(HOST_OUT_EXECUTABLES)/brotli$(HOST_EXECUTABLE_SUFFIX)
 ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG)))
 MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
 else
@@ -608,6 +607,9 @@
 LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)
 BUILD_IMAGE := $(HOST_OUT_EXECUTABLES)/build_image$(HOST_EXECUTABLE_SUFFIX)
 BUILD_SUPER_IMAGE := $(HOST_OUT_EXECUTABLES)/build_super_image$(HOST_EXECUTABLE_SUFFIX)
+MAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX)
+OTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX)
+SPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX)
 
 PROGUARD_HOME := external/proguard
 PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh
@@ -623,7 +625,6 @@
 FUTILITY := $(HOST_OUT_EXECUTABLES)/futility-host
 VBOOT_SIGNER := $(HOST_OUT_EXECUTABLES)/vboot_signer
 FEC := $(HOST_OUT_EXECUTABLES)/fec
-BRILLO_UPDATE_PAYLOAD := $(HOST_OUT_EXECUTABLES)/brillo_update_payload
 
 DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump$(BUILD_EXECUTABLE_SUFFIX)
 PROFMAN := $(HOST_OUT_EXECUTABLES)/profman
diff --git a/core/definitions.mk b/core/definitions.mk
index 16bec84..f32a995 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1107,7 +1107,7 @@
 ###########################################################
 define c-includes
 $(addprefix -I , $(PRIVATE_C_INCLUDES)) \
-$$(cat $(PRIVATE_IMPORT_INCLUDES))\
+$(foreach i,$(PRIVATE_IMPORTED_INCLUDES),$(EXPORTS.$(i)))\
 $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\
     $(addprefix -I ,\
         $(filter-out $(PRIVATE_C_INCLUDES), \
diff --git a/core/main.mk b/core/main.mk
index 9f4b86c..5e25af4 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1,14 +1,3 @@
-# Only use ANDROID_BUILD_SHELL to wrap around bash.
-# DO NOT use other shells such as zsh.
-ifdef ANDROID_BUILD_SHELL
-SHELL := $(ANDROID_BUILD_SHELL)
-else
-# Use bash, not whatever shell somebody has installed as /bin/sh
-# This is repeated in config.mk, since envsetup.sh runs that file
-# directly.
-SHELL := /bin/bash
-endif
-
 ifndef KATI
 $(warning Calling make directly is no longer supported.)
 $(warning Either use 'envsetup.sh; m' or 'build/soong/soong_ui.bash --make-mode')
@@ -22,9 +11,6 @@
 # the top of the source tree, for example when "make -C" is used in m/mm/mmm.
 PWD := $(shell pwd)
 
-TOP := .
-TOPDIR :=
-
 # This is the default target.  It must be the first declared target.
 .PHONY: droid
 DEFAULT_GOAL := droid
@@ -973,6 +959,27 @@
 endif
 
 # -------------------------------------------------------------------
+# Handle exported/imported includes
+
+# Recursively calculate flags
+$(foreach export,$(EXPORTS_LIST), \
+  $(eval EXPORTS.$$(export) = $$(EXPORTS.$(export).FLAGS) \
+    $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORTS.$(dep)))))
+
+# Recursively calculate dependencies
+$(foreach export,$(EXPORTS_LIST), \
+  $(eval EXPORT_DEPS.$$(export) = $$(EXPORTS.$(export).DEPS) \
+    $(foreach dep,$(EXPORTS.$(export).REEXPORT),$$(EXPORT_DEPS.$(dep)))))
+
+# Converts the recursive variables to simple variables so that we don't have to
+# evaluate them for every .o rule
+$(foreach export,$(EXPORTS_LIST),$(eval EXPORTS.$$(export) := $$(strip $$(EXPORTS.$$(export)))))
+$(foreach export,$(EXPORTS_LIST),$(eval EXPORT_DEPS.$$(export) := $$(sort $$(EXPORT_DEPS.$$(export)))))
+
+# Add dependencies
+$(foreach export,$(EXPORTS_LIST),$(eval $(call add-dependency,$$(EXPORTS.$$(export).USERS),$$(EXPORT_DEPS.$$(export)))))
+
+# -------------------------------------------------------------------
 # Figure out our module sets.
 #
 # Of the modules defined by the component makefiles,
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_prebuilt.mk
index 301f985..34dd3e8 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_prebuilt.mk
@@ -65,16 +65,9 @@
 
 ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES HEADER_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
   # Soong module is a static or shared library
-  export_includes := $(intermediates)/export_includes
-  $(export_includes): PRIVATE_EXPORT_CFLAGS := $(LOCAL_EXPORT_CFLAGS)
-  $(export_includes): $(LOCAL_EXPORT_C_INCLUDE_DEPS)
-	@echo Export includes file: $< -- $@
-	$(hide) mkdir -p $(dir $@) && rm -f $@
-  ifdef LOCAL_EXPORT_CFLAGS
-	$(hide) echo "$(PRIVATE_EXPORT_CFLAGS)" >$@
-  else
-	$(hide) touch $@
-  endif
+  EXPORTS_LIST := $(EXPORTS_LIST) $(intermediates)
+  EXPORTS.$(intermediates).FLAGS := $(LOCAL_EXPORT_CFLAGS)
+  EXPORTS.$(intermediates).DEPS := $(LOCAL_EXPORT_C_INCLUDE_DEPS)
 
   ifdef LOCAL_SOONG_TOC
     $(eval $(call copy-one-file,$(LOCAL_SOONG_TOC),$(LOCAL_BUILT_MODULE).toc))
diff --git a/core/tasks/vts-core-tests.mk b/core/tasks/vts-core-tests.mk
new file mode 100644
index 0000000..919354c
--- /dev/null
+++ b/core/tasks/vts-core-tests.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.PHONY: vts-core
+
+vts-core-zip := $(PRODUCT_OUT)/vts-core-tests.zip
+# Create an artifact to include a list of test config files in vts-core.
+vts-core-list-zip := $(PRODUCT_OUT)/vts-core_list.zip
+# Create an artifact to include all test config files in vts-core.
+vts-core-configs-zip := $(PRODUCT_OUT)/vts-core_configs.zip
+my_host_shared_lib_for_vts_core := $(call copy-many-files,$(COMPATIBILITY.vts-core.HOST_SHARED_LIBRARY.FILES))
+$(vts-core-zip) : .KATI_IMPLICIT_OUTPUTS := $(vts-core-list-zip) $(vts-core-configs-zip)
+$(vts-core-zip) : PRIVATE_vts_core_list := $(PRODUCT_OUT)/vts-core_list
+$(vts-core-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_vts_core)
+$(vts-core-zip) : $(COMPATIBILITY.vts-core.FILES) $(my_host_shared_lib_for_vts_core) $(SOONG_ZIP)
+	echo $(sort $(COMPATIBILITY.vts-core.FILES)) | tr " " "\n" > $@.list
+	grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
+	grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true
+	$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+	  echo $$shared_lib >> $@-host.list; \
+	done
+	grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
+	grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true
+	$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list
+	$(hide) $(SOONG_ZIP) -d -o $(vts-core-configs-zip) \
+	  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \
+	  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list
+	rm -f $(PRIVATE_vts_core_list)
+	$(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_vts_core_list)
+	$(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_vts_core_list)
+	$(hide) $(SOONG_ZIP) -d -o $(vts-core-list-zip) -C $(dir $@) -f $(PRIVATE_vts_core_list)
+	rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \
+	  $(PRIVATE_vts_core_list)
+
+vts-core: $(vts-core-zip)
+$(call dist-for-goals, vts-core, $(vts-core-zip) $(vts-core-list-zip) $(vts-core-configs-zip))
diff --git a/envsetup.sh b/envsetup.sh
index 9198ee5..0392e86 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -285,6 +285,9 @@
     fi
     # and in with the new
     export ANDROID_PYTHONPATH=$T/development/python-packages:
+    if [ -n $VENDOR_PYTHONPATH  ]; then
+        ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
+    fi
     export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH
 
     export ANDROID_JAVA_HOME=$(get_abs_build_var ANDROID_JAVA_HOME)
@@ -1552,6 +1555,7 @@
 #
 # This allows loading only approved vendorsetup.sh files
 function source_vendorsetup() {
+    unset VENDOR_PYTHONPATH
     allowed=
     for f in $(find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do
         if [ -n "$allowed" ]; then
diff --git a/target/board/BoardConfigEmuCommon.mk b/target/board/BoardConfigEmuCommon.mk
index 76cb470..ac21918 100644
--- a/target/board/BoardConfigEmuCommon.mk
+++ b/target/board/BoardConfigEmuCommon.mk
@@ -37,9 +37,7 @@
   BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions
   BOARD_EMULATOR_DYNAMIC_PARTITIONS_PARTITION_LIST := \
       system \
-      vendor \
-      product \
-      system_ext
+      vendor
 
   # 3G
   BOARD_EMULATOR_DYNAMIC_PARTITIONS_SIZE := 3221225472
@@ -57,11 +55,6 @@
   BOARD_VENDORIMAGE_PARTITION_SIZE := 146800640
 endif
 
-TARGET_COPY_OUT_PRODUCT := product
-BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4
-TARGET_COPY_OUT_SYSTEM_EXT := system_ext
-BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4
-
 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
 BOARD_FLASH_BLOCK_SIZE := 512
 DEVICE_MATRIX_FILE   := device/generic/goldfish/compatibility_matrix.xml
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index 297f350..cc4785a 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -40,7 +40,6 @@
 endif
 
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
-    root/init.zygote32_64.rc \
     root/init.zygote64_32.rc \
 
 #
@@ -60,14 +59,6 @@
 #
 ifeq (aosp_arm64,$(TARGET_PRODUCT))
 $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
-
-# Copy different zygote settings for vendor.img to select by setting property
-# ro.zygote=zygote64_32 or ro.zygote=zygote32_64:
-#   1. 64-bit primary, 32-bit secondary OR
-#   2. 32-bit primary, 64-bit secondary
-# init.zygote64_32.rc is in the core_64_bit.mk below
-PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.zygote32_64.rc:root/init.zygote32_64.rc
 endif
 
 
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 74f9394..a471702 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -40,7 +40,6 @@
 endif
 
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
-    root/init.zygote32_64.rc \
     root/init.zygote64_32.rc \
 
 #
@@ -60,14 +59,6 @@
 #
 ifeq (aosp_x86_64,$(TARGET_PRODUCT))
 $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
-
-# Copy different zygote settings for vendor.img to select by setting property
-# ro.zygote=zygote64_32 or ro.zygote=zygote32_64:
-#   1. 64-bit primary, 32-bit secondary OR
-#   2. 32-bit primary, 64-bit secondary
-# init.zygote64_32.rc is in the core_64_bit.mk below
-PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.zygote32_64.rc:root/init.zygote32_64.rc
 endif
 
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index c256641..60646c3 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -54,6 +54,7 @@
     charger \
     cmd \
     com.android.conscrypt \
+    com.android.i18n \
     com.android.location.provider \
     com.android.resolv \
     com.android.neuralnetworks \
@@ -289,7 +290,7 @@
     e2fsck \
     fastboot \
     flags_health_check \
-    icu-data_host_runtime_apex \
+    icu-data_host_i18n_apex \
     icu_tzdata.dat_host_tzdata_apex \
     incident_report \
     ld.mc \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 4233c1c..c57cc3f 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -36,7 +36,7 @@
     make_f2fs \
 
 PRODUCT_HOST_PACKAGES += \
-    icu-data_host_runtime_apex
+    icu-data_host_i18n_apex
 
 # Base modules and settings for the vendor partition.
 PRODUCT_PACKAGES += \
diff --git a/target/product/gsi_arm64.mk b/target/product/gsi_arm64.mk
index b0225a3..09fb633 100644
--- a/target/product/gsi_arm64.mk
+++ b/target/product/gsi_arm64.mk
@@ -24,7 +24,6 @@
 PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
 
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
-    root/init.zygote32_64.rc \
     root/init.zygote64_32.rc \
 
 #
@@ -37,14 +36,6 @@
 #
 $(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
 
-# Copy different zygote settings for vendor.img to select by setting property
-# ro.zygote=zygote64_32 or ro.zygote=zygote32_64:
-#   1. 64-bit primary, 32-bit secondary OR
-#   2. 32-bit primary, 64-bit secondary
-# init.zygote64_32.rc is in the core_64_bit.mk below
-PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.zygote32_64.rc:root/init.zygote32_64.rc
-
 
 PRODUCT_NAME := gsi_arm64
 PRODUCT_DEVICE := gsi_arm64
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index d88ad35..4c471db 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -56,3 +56,19 @@
 
 # Support addtional P VNDK packages
 PRODUCT_EXTRA_VNDK_VERSIONS := 28
+
+# The 64 bits GSI build targets inhiert core_64_bit.mk to enable 64 bits and
+# include the init.zygote64_32.rc.
+# 64 bits GSI for releasing need to includes different zygote settings for
+# vendor.img to select by setting property ro.zygote=zygote64_32 or
+# ro.zygote=zygote32_64:
+#   1. 64-bit primary, 32-bit secondary, or
+#   2. 32-bit primary, 64-bit secondary
+# Here includes the init.zygote32_64.rc if it had inhierted core_64_bit.mk.
+ifeq (true|true,$(TARGET_SUPPORTS_32_BIT_APPS)|$(TARGET_SUPPORTS_64_BIT_APPS))
+PRODUCT_COPY_FILES += \
+    system/core/rootdir/init.zygote32_64.rc:root/init.zygote32_64.rc
+
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
+    root/init.zygote32_64.rc
+endif
diff --git a/tools/fs_config/fs_config_generator.py b/tools/fs_config/fs_config_generator.py
index e1aafc9..2956b11 100755
--- a/tools/fs_config/fs_config_generator.py
+++ b/tools/fs_config/fs_config_generator.py
@@ -1037,7 +1037,7 @@
             caps_split = caps.split(',')
             for cap in caps_split:
                 if cap not in caps_dict:
-                    sys.exit('Unkonwn cap "%s" found!' % cap)
+                    sys.exit('Unknown cap "%s" found!' % cap)
                 caps_value += 1 << caps_dict[cap]
 
         path_length_with_null = len(path) + 1
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 40cdce8..3732b78 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -25,6 +25,14 @@
 }
 
 python_library_host {
+    name: "releasetools_build_super_image",
+    defaults: ["releasetools_library_defaults"],
+    srcs: [
+        "build_super_image.py",
+    ],
+}
+
+python_library_host {
     name: "releasetools_common",
     defaults: ["releasetools_library_defaults"],
     srcs: [
@@ -107,25 +115,63 @@
 }
 
 python_binary_host {
+    name: "make_recovery_patch",
+    defaults: ["releasetools_binary_defaults"],
+    srcs: [
+        "make_recovery_patch.py",
+    ],
+    libs: [
+        "releasetools_common",
+    ],
+}
+
+python_binary_host {
     name: "merge_builds",
     defaults: ["releasetools_binary_defaults"],
     srcs: [
-        "build_super_image.py",
         "merge_builds.py",
     ],
     main: "merge_builds.py",
     libs: [
+        "releasetools_build_super_image",
         "releasetools_common",
     ],
 }
 
+python_binary_host {
+    name: "ota_from_target_files",
+    defaults: ["releasetools_binary_defaults"],
+    srcs: [
+        "edify_generator.py",
+        "ota_from_target_files.py",
+        "target_files_diff.py",
+    ],
+    main: "ota_from_target_files.py",
+    libs: [
+        "releasetools_common",
+        "releasetools_verity_utils",
+    ],
+    required: [
+        "brillo_update_payload",
+    ],
+}
+
+python_binary_host {
+    name: "sparse_img",
+    defaults: ["releasetools_binary_defaults"],
+    srcs: [
+        "rangelib.py",
+        "sparse_img.py",
+    ],
+    main: "sparse_img.py",
+}
+
 python_defaults {
     name: "releasetools_test_defaults",
     srcs: [
         "add_img_to_target_files.py",
         "apex_utils.py",
         "build_image.py",
-        "build_super_image.py",
         "check_ota_package_signature.py",
         "check_target_files_signatures.py",
         "edify_generator.py",
@@ -142,6 +188,7 @@
         "test_*.py",
     ],
     libs: [
+        "releasetools_build_super_image",
         "releasetools_common",
         "releasetools_verity_utils",
     ],
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index bdb34b8..23ae29f 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -391,28 +391,6 @@
   img.Write()
 
 
-def AppendVBMetaArgsForPartition(cmd, partition, image):
-  """Appends the VBMeta arguments for partition.
-
-  It sets up the VBMeta argument by including the partition descriptor from the
-  given 'image', or by configuring the partition as a chained partition.
-
-  Args:
-    cmd: A list of command args that will be used to generate the vbmeta image.
-        The argument for the partition will be appended to the list.
-    partition: The name of the partition (e.g. "system").
-    image: The path to the partition image.
-  """
-  # Check if chain partition is used.
-  key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
-  if key_path:
-    chained_partition_arg = common.GetAvbChainedPartitionArg(
-        partition, OPTIONS.info_dict)
-    cmd.extend(["--chain_partition", chained_partition_arg])
-  else:
-    cmd.extend(["--include_descriptors_from_image", image])
-
-
 def AddVBMeta(output_zip, partitions, name, needed_partitions):
   """Creates a VBMeta image and stores it in output_zip.
 
@@ -442,45 +420,7 @@
     logger.info("%s.img already exists; not rebuilding...", name)
     return img.name
 
-  avbtool = OPTIONS.info_dict["avb_avbtool"]
-  cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
-  common.AppendAVBSigningArgs(cmd, name)
-
-  for partition, path in partitions.items():
-    if partition not in needed_partitions:
-      continue
-    assert (partition in common.AVB_PARTITIONS or
-            partition in common.AVB_VBMETA_PARTITIONS), \
-        'Unknown partition: {}'.format(partition)
-    assert os.path.exists(path), \
-        'Failed to find {} for {}'.format(path, partition)
-    AppendVBMetaArgsForPartition(cmd, partition, path)
-
-  args = OPTIONS.info_dict.get("avb_{}_args".format(name))
-  if args and args.strip():
-    split_args = shlex.split(args)
-    for index, arg in enumerate(split_args[:-1]):
-      # Sanity check that the image file exists. Some images might be defined
-      # as a path relative to source tree, which may not be available at the
-      # same location when running this script (we have the input target_files
-      # zip only). For such cases, we additionally scan other locations (e.g.
-      # IMAGES/, RADIO/, etc) before bailing out.
-      if arg == '--include_descriptors_from_image':
-        image_path = split_args[index + 1]
-        if os.path.exists(image_path):
-          continue
-        found = False
-        for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
-          alt_path = os.path.join(
-              OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
-          if os.path.exists(alt_path):
-            split_args[index + 1] = alt_path
-            found = True
-            break
-        assert found, 'Failed to find {}'.format(image_path)
-    cmd.extend(split_args)
-
-  common.RunAndCheckOutput(cmd)
+  common.BuildVBMeta(img.name, partitions, name, needed_partitions)
   img.Write()
   return img.name
 
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 1175688..2401e46 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -625,6 +625,33 @@
     cmd.extend(["--salt", avb_salt])
 
 
+def GetAvbPartitionArg(partition, image, info_dict = None):
+  """Returns the VBMeta arguments for partition.
+
+  It sets up the VBMeta argument by including the partition descriptor from the
+  given 'image', or by configuring the partition as a chained partition.
+
+  Args:
+    partition: The name of the partition (e.g. "system").
+    image: The path to the partition image.
+    info_dict: A dict returned by common.LoadInfoDict(). Will use
+        OPTIONS.info_dict if None has been given.
+
+  Returns:
+    A list of VBMeta arguments.
+  """
+  if info_dict is None:
+    info_dict = OPTIONS.info_dict
+
+  # Check if chain partition is used.
+  key_path = info_dict.get("avb_" + partition + "_key_path")
+  if key_path:
+    chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)
+    return ["--chain_partition", chained_partition_arg]
+  else:
+    return ["--include_descriptors_from_image", image]
+
+
 def GetAvbChainedPartitionArg(partition, info_dict, key=None):
   """Constructs and returns the arg to build or verify a chained partition.
 
@@ -647,6 +674,65 @@
   return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
 
 
+def BuildVBMeta(image_path, partitions, name, needed_partitions):
+  """Creates a VBMeta image.
+
+  It generates the requested VBMeta image. The requested image could be for
+  top-level or chained VBMeta image, which is determined based on the name.
+
+  Args:
+    image_path: The output path for the new VBMeta image.
+    partitions: A dict that's keyed by partition names with image paths as
+        values. Only valid partition names are accepted, as listed in
+        common.AVB_PARTITIONS.
+    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
+    needed_partitions: Partitions whose descriptors should be included into the
+        generated VBMeta image.
+
+  Raises:
+    AssertionError: On invalid input args.
+  """
+  avbtool = OPTIONS.info_dict["avb_avbtool"]
+  cmd = [avbtool, "make_vbmeta_image", "--output", image_path]
+  AppendAVBSigningArgs(cmd, name)
+
+  for partition, path in partitions.items():
+    if partition not in needed_partitions:
+      continue
+    assert (partition in AVB_PARTITIONS or
+            partition in AVB_VBMETA_PARTITIONS), \
+        'Unknown partition: {}'.format(partition)
+    assert os.path.exists(path), \
+        'Failed to find {} for {}'.format(path, partition)
+    cmd.extend(GetAvbPartitionArg(partition, path))
+
+  args = OPTIONS.info_dict.get("avb_{}_args".format(name))
+  if args and args.strip():
+    split_args = shlex.split(args)
+    for index, arg in enumerate(split_args[:-1]):
+      # Sanity check that the image file exists. Some images might be defined
+      # as a path relative to source tree, which may not be available at the
+      # same location when running this script (we have the input target_files
+      # zip only). For such cases, we additionally scan other locations (e.g.
+      # IMAGES/, RADIO/, etc) before bailing out.
+      if arg == '--include_descriptors_from_image':
+        image_path = split_args[index + 1]
+        if os.path.exists(image_path):
+          continue
+        found = False
+        for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
+          alt_path = os.path.join(
+              OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
+          if os.path.exists(alt_path):
+            split_args[index + 1] = alt_path
+            found = True
+            break
+        assert found, 'Failed to find {}'.format(image_path)
+    cmd.extend(split_args)
+
+  RunAndCheckOutput(cmd)
+
+
 def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
                         has_ramdisk=False, two_step_image=False):
   """Build a bootable image from the specified sourcedir.
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index e4c9852..4b94ad8 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -15,17 +15,12 @@
 # limitations under the License.
 
 """
-Given target-files, produces an image zipfile suitable for use
-with 'fastboot update'.
+Given an input target-files, produces an image zipfile suitable for use with
+'fastboot update'.
 
 Usage:  img_from_target_files [flags] input_target_files output_image_zip
 
-input_target_files: one of the following:
-  - directory containing extracted target files. It will load info from
-    OTA/android-info.txt, META/misc_info.txt and build the image zipfile using
-    images from IMAGES/.
-  - target files package. Same as above, but extracts the archive before
-    building the image zipfile.
+input_target_files: Path to the input target_files zip.
 
 Flags:
   -z  (--bootable_zip)
@@ -38,7 +33,6 @@
 
 import logging
 import os
-import shutil
 import sys
 import zipfile
 
@@ -55,12 +49,10 @@
 
 
 def LoadOptions(input_file):
-  """
-  Load information from input_file to OPTIONS.
+  """Loads information from input_file to OPTIONS.
 
   Args:
-    input_file: A Zipfile instance of input zip file, or path to the directory
-      of extracted zip.
+    input_file: Path to the root dir of an extracted target_files zip.
   """
   info = OPTIONS.info_dict = common.LoadInfoDict(input_file)
 
@@ -75,15 +67,14 @@
 
 
 def CopyInfo(input_tmp, output_zip):
-  """Copy the android-info.txt file from the input to the output."""
+  """Copies the android-info.txt file from the input to the output."""
   common.ZipWrite(
       output_zip, os.path.join(input_tmp, "OTA", "android-info.txt"),
       "android-info.txt")
 
 
 def CopyUserImages(input_tmp, output_zip):
-  """
-  Copy user images from the unzipped input and write to output_zip.
+  """Copies user images from the unzipped input and write to output_zip.
 
   Args:
     input_tmp: path to the unzipped input.
@@ -113,9 +104,9 @@
 
 
 def WriteSuperImages(input_tmp, output_zip):
-  """
-  Write super images from the unzipped input and write to output_zip. This is
-  only done if super_image_in_update_package is set to "true".
+  """Writes super images from the unzipped input into output_zip.
+
+  This is only done if super_image_in_update_package is set to "true".
 
   - For retrofit dynamic partition devices, copy split super images from target
     files package.
@@ -148,6 +139,40 @@
     common.ZipWrite(output_zip, super_file, "super.img")
 
 
+def ImgFromTargetFiles(input_file, output_file):
+  """Creates an image archive from the input target_files zip.
+
+  Args:
+    input_file: Path to the input target_files zip.
+    output_file: Output filename.
+
+  Raises:
+    ValueError: On invalid input.
+  """
+  if not zipfile.is_zipfile(input_file):
+    raise ValueError("%s is not a valid zipfile" % input_file)
+
+  logger.info("Building image zip from target files zip.")
+
+  # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
+  # However, common.LoadInfoDict() may read additional files under BOOT/,
+  # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
+  input_tmp = common.UnzipTemp(input_file)
+
+  LoadOptions(input_tmp)
+  output_zip = zipfile.ZipFile(
+      output_file, "w", compression=zipfile.ZIP_DEFLATED,
+      allowZip64=not OPTIONS.sparse_userimages)
+
+  try:
+    CopyInfo(input_tmp, output_zip)
+    CopyUserImages(input_tmp, output_zip)
+    WriteSuperImages(input_tmp, output_zip)
+  finally:
+    logger.info("cleaning up...")
+    common.ZipClose(output_zip)
+
+
 def main(argv):
   # This allows modifying the value from inner function.
   bootable_only_array = [False]
@@ -172,30 +197,7 @@
 
   common.InitLogging()
 
-  target_files = args[0]
-  if os.path.isdir(target_files):
-    logger.info("Building image zip from extracted target files.")
-    OPTIONS.input_tmp = target_files
-  elif zipfile.is_zipfile(target_files):
-    logger.info("Building image zip from target files zip.")
-    # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
-    # However, common.LoadInfoDict() may read additional files under BOOT/,
-    # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
-    OPTIONS.input_tmp = common.UnzipTemp(target_files)
-  else:
-    raise ValueError("%s is not a valid path." % target_files)
-
-  LoadOptions(OPTIONS.input_tmp)
-  output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED,
-                               allowZip64=not OPTIONS.sparse_userimages)
-
-  try:
-    CopyInfo(OPTIONS.input_tmp, output_zip)
-    CopyUserImages(OPTIONS.input_tmp, output_zip)
-    WriteSuperImages(OPTIONS.input_tmp, output_zip)
-  finally:
-    logger.info("cleaning up...")
-    common.ZipClose(output_zip)
+  ImgFromTargetFiles(args[0], args[1])
 
   logger.info("done.")
 
diff --git a/tools/releasetools/make_recovery_patch b/tools/releasetools/make_recovery_patch
deleted file mode 120000
index 45cec08..0000000
--- a/tools/releasetools/make_recovery_patch
+++ /dev/null
@@ -1 +0,0 @@
-make_recovery_patch.py
\ No newline at end of file
diff --git a/tools/releasetools/make_recovery_patch.py b/tools/releasetools/make_recovery_patch.py
old mode 100755
new mode 100644
diff --git a/tools/releasetools/merge_builds.py b/tools/releasetools/merge_builds.py
index 7724d6f..ca348cf 100644
--- a/tools/releasetools/merge_builds.py
+++ b/tools/releasetools/merge_builds.py
@@ -24,9 +24,8 @@
 vendor partial build determines whether the merged result supports DAP.
 
 This script does not require builds to be built with 'make dist'.
-This script assumes that images other than super_empty.img do not require
-regeneration, including vbmeta images.
-TODO(b/137853921): Add support for regenerating vbmeta images.
+This script regenerates super_empty.img and vbmeta.img if necessary. Other
+images are assumed to not require regeneration.
 
 Usage: merge_builds.py [args]
 
@@ -39,6 +38,15 @@
 
   --product_out_vendor product_out_vendor_path
       Path to out/target/product/<vendor build>.
+
+  --build_vbmeta
+      If provided, vbmeta.img will be regenerated in out/target/product/<vendor
+      build>.
+
+  --framework_misc_info_keys
+      The optional path to a newline-separated config file containing keys to
+      obtain from the framework instance of misc_info.txt, used for creating
+      vbmeta.img. The remaining keys come from the vendor instance.
 """
 from __future__ import print_function
 
@@ -55,6 +63,8 @@
 OPTIONS.framework_images = ("system",)
 OPTIONS.product_out_framework = None
 OPTIONS.product_out_vendor = None
+OPTIONS.build_vbmeta = False
+OPTIONS.framework_misc_info_keys = None
 
 
 def CreateImageSymlinks():
@@ -82,6 +92,7 @@
   # super_empty.img from the framework build.
   if (framework_dict.get("use_dynamic_partitions") == "true") and (
       vendor_dict.get("use_dynamic_partitions") == "true"):
+    logger.info("Building super_empty.img.")
     merged_dict = dict(vendor_dict)
     merged_dict.update(
         common.MergeDynamicPartitionInfoDicts(
@@ -96,10 +107,52 @@
     build_super_image.BuildSuperImage(merged_dict, output_super_empty_path)
 
 
+def BuildVBMeta():
+  logger.info("Building vbmeta.img.")
+
+  framework_dict = common.LoadDictionaryFromFile(
+      os.path.join(OPTIONS.product_out_framework, "misc_info.txt"))
+  vendor_dict = common.LoadDictionaryFromFile(
+      os.path.join(OPTIONS.product_out_vendor, "misc_info.txt"))
+  merged_dict = dict(vendor_dict)
+  if OPTIONS.framework_misc_info_keys:
+    for key in common.LoadListFromFile(OPTIONS.framework_misc_info_keys):
+      merged_dict[key] = framework_dict[key]
+
+  # Build vbmeta.img using partitions in product_out_vendor.
+  partitions = {}
+  for partition in common.AVB_PARTITIONS:
+    partition_path = os.path.join(OPTIONS.product_out_vendor,
+                                  "%s.img" % partition)
+    if os.path.exists(partition_path):
+      partitions[partition] = partition_path
+
+  # vbmeta_partitions includes the partitions that should be included into
+  # top-level vbmeta.img, which are the ones that are not included in any
+  # chained VBMeta image plus the chained VBMeta images themselves.
+  vbmeta_partitions = common.AVB_PARTITIONS[:]
+  for partition in common.AVB_VBMETA_PARTITIONS:
+    chained_partitions = merged_dict.get("avb_%s" % partition, "").strip()
+    if chained_partitions:
+      partitions[partition] = os.path.join(OPTIONS.product_out_vendor,
+                                           "%s.img" % partition)
+      vbmeta_partitions = [
+          item for item in vbmeta_partitions
+          if item not in chained_partitions.split()
+      ]
+      vbmeta_partitions.append(partition)
+
+  output_vbmeta_path = os.path.join(OPTIONS.product_out_vendor, "vbmeta.img")
+  OPTIONS.info_dict = merged_dict
+  common.BuildVBMeta(output_vbmeta_path, partitions, "vbmeta",
+                     vbmeta_partitions)
+
+
 def MergeBuilds():
   CreateImageSymlinks()
   BuildSuperEmpty()
-  # TODO(b/137853921): Add support for regenerating vbmeta images.
+  if OPTIONS.build_vbmeta:
+    BuildVBMeta()
 
 
 def main():
@@ -112,6 +165,10 @@
       OPTIONS.product_out_framework = a
     elif o == "--product_out_vendor":
       OPTIONS.product_out_vendor = a
+    elif o == "--build_vbmeta":
+      OPTIONS.build_vbmeta = True
+    elif o == "--framework_misc_info_keys":
+      OPTIONS.framework_misc_info_keys = a
     else:
       return False
     return True
@@ -123,6 +180,8 @@
           "framework_images=",
           "product_out_framework=",
           "product_out_vendor=",
+          "build_vbmeta",
+          "framework_misc_info_keys=",
       ],
       extra_option_handler=option_handler)
 
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 81b95c8..cfffbc7 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -94,7 +94,6 @@
 logger = logging.getLogger(__name__)
 
 OPTIONS = common.OPTIONS
-OPTIONS.verbose = True
 OPTIONS.framework_target_files = None
 OPTIONS.framework_item_list = None
 OPTIONS.framework_misc_info_keys = None
@@ -1057,6 +1056,9 @@
     common.Usage(__doc__)
     sys.exit(1)
 
+  # Always turn on verbose logging.
+  OPTIONS.verbose = True
+
   if OPTIONS.framework_item_list:
     framework_item_list = common.LoadListFromFile(OPTIONS.framework_item_list)
   else:
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 4472b4a..9715aa1 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -241,6 +241,9 @@
 DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
 AB_PARTITIONS = 'META/ab_partitions.txt'
 UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
+# Files to be unzipped for target diffing purpose.
+TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
+                                'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*']
 RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
 
 
@@ -517,7 +520,7 @@
     """Signs the given input file. Returns the output filename."""
     out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
     cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
-    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    common.RunAndCheckOutput(cmd)
     return out_file
 
 
@@ -539,6 +542,15 @@
     self.payload_properties = None
     self.secondary = secondary
 
+  def _Run(self, cmd):  # pylint: disable=no-self-use
+    # Don't pipe (buffer) the output if verbose is set. Let
+    # brillo_update_payload write to stdout/stderr directly, so its progress can
+    # be monitored.
+    if OPTIONS.verbose:
+      common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    else:
+      common.RunAndCheckOutput(cmd)
+
   def Generate(self, target_file, source_file=None, additional_args=None):
     """Generates a payload from the given target-files zip(s).
 
@@ -559,7 +571,7 @@
     if source_file is not None:
       cmd.extend(["--source_image", source_file])
     cmd.extend(additional_args)
-    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    self._Run(cmd)
 
     self.payload_file = payload_file
     self.payload_properties = None
@@ -583,7 +595,7 @@
            "--signature_size", str(payload_signer.key_size),
            "--metadata_hash_file", metadata_sig_file,
            "--payload_hash_file", payload_sig_file]
-    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    self._Run(cmd)
 
     # 2. Sign the hashes.
     signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
@@ -598,7 +610,7 @@
            "--signature_size", str(payload_signer.key_size),
            "--metadata_signature_file", signed_metadata_sig_file,
            "--payload_signature_file", signed_payload_sig_file]
-    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    self._Run(cmd)
 
     # 4. Dump the signed payload properties.
     properties_file = common.MakeTempFile(prefix="payload-properties-",
@@ -606,7 +618,7 @@
     cmd = ["brillo_update_payload", "properties",
            "--payload", signed_payload_file,
            "--properties_file", properties_file]
-    common.RunAndCheckOutput(cmd, stdout=None, stderr=None)
+    self._Run(cmd)
 
     if self.secondary:
       with open(properties_file, "a") as f:
@@ -1996,8 +2008,7 @@
   return target_file
 
 
-def WriteABOTAPackageWithBrilloScript(target_file, output_file,
-                                      source_file=None):
+def GenerateAbOtaPackage(target_file, output_file, source_file=None):
   """Generates an Android OTA package that has A/B update payload."""
   # Stage the output zip package for package signing.
   if not OPTIONS.no_signing:
@@ -2095,6 +2106,66 @@
   FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
 
 
+def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
+  """Generates a non-A/B OTA package."""
+  # Sanity check the loaded info dicts first.
+  if OPTIONS.info_dict.get("no_recovery") == "true":
+    raise common.ExternalError(
+        "--- target build has specified no recovery ---")
+
+  # Non-A/B OTAs rely on /cache partition to store temporary files.
+  cache_size = OPTIONS.info_dict.get("cache_size")
+  if cache_size is None:
+    logger.warning("--- can't determine the cache partition size ---")
+  OPTIONS.cache_size = cache_size
+
+  if OPTIONS.extra_script is not None:
+    with open(OPTIONS.extra_script) as fp:
+      OPTIONS.extra_script = fp.read()
+
+  if OPTIONS.extracted_input is not None:
+    OPTIONS.input_tmp = OPTIONS.extracted_input
+  else:
+    logger.info("unzipping target target-files...")
+    OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
+  OPTIONS.target_tmp = OPTIONS.input_tmp
+
+  # If the caller explicitly specified the device-specific extensions path via
+  # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
+  # is present in the target target_files. Otherwise, take the path of the file
+  # from 'tool_extensions' in the info dict and look for that in the local
+  # filesystem, relative to the current directory.
+  if OPTIONS.device_specific is None:
+    from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
+    if os.path.exists(from_input):
+      logger.info("(using device-specific extensions from target_files)")
+      OPTIONS.device_specific = from_input
+    else:
+      OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
+
+  if OPTIONS.device_specific is not None:
+    OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
+
+  # Generate a full OTA.
+  if source_file is None:
+    with zipfile.ZipFile(target_file) as input_zip:
+      WriteFullOTAPackage(
+          input_zip,
+          output_file)
+
+  # Generate an incremental OTA.
+  else:
+    logger.info("unzipping source target-files...")
+    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:
+      WriteBlockIncrementalOTAPackage(
+          input_zip,
+          source_zip,
+          output_file)
+
+
 def main(argv):
 
   def option_handler(o, a):
@@ -2269,76 +2340,29 @@
     OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
 
   if ab_update:
-    WriteABOTAPackageWithBrilloScript(
+    GenerateAbOtaPackage(
         target_file=args[0],
         output_file=args[1],
         source_file=OPTIONS.incremental_source)
 
-    logger.info("done.")
-    return
-
-  # Sanity check the loaded info dicts first.
-  if OPTIONS.info_dict.get("no_recovery") == "true":
-    raise common.ExternalError(
-        "--- target build has specified no recovery ---")
-
-  # Non-A/B OTAs rely on /cache partition to store temporary files.
-  cache_size = OPTIONS.info_dict.get("cache_size")
-  if cache_size is None:
-    logger.warning("--- can't determine the cache partition size ---")
-  OPTIONS.cache_size = cache_size
-
-  if OPTIONS.extra_script is not None:
-    with open(OPTIONS.extra_script) as fp:
-      OPTIONS.extra_script = fp.read()
-
-  if OPTIONS.extracted_input is not None:
-    OPTIONS.input_tmp = OPTIONS.extracted_input
   else:
-    logger.info("unzipping target target-files...")
-    OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
-  OPTIONS.target_tmp = OPTIONS.input_tmp
+    GenerateNonAbOtaPackage(
+        target_file=args[0],
+        output_file=args[1],
+        source_file=OPTIONS.incremental_source)
 
-  # If the caller explicitly specified the device-specific extensions path via
-  # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
-  # is present in the target target_files. Otherwise, take the path of the file
-  # from 'tool_extensions' in the info dict and look for that in the local
-  # filesystem, relative to the current directory.
-  if OPTIONS.device_specific is None:
-    from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
-    if os.path.exists(from_input):
-      logger.info("(using device-specific extensions from target_files)")
-      OPTIONS.device_specific = from_input
-    else:
-      OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
+  # Post OTA generation works.
+  if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
+    logger.info("Generating diff logs...")
+    logger.info("Unzipping target-files for diffing...")
+    target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
+    source_dir = common.UnzipTemp(
+        OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
 
-  if OPTIONS.device_specific is not None:
-    OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
-
-  # Generate a full OTA.
-  if OPTIONS.incremental_source is None:
-    with zipfile.ZipFile(args[0], 'r') as input_zip:
-      WriteFullOTAPackage(
-          input_zip,
-          output_file=args[1])
-
-  # Generate an incremental OTA.
-  else:
-    logger.info("unzipping source target-files...")
-    OPTIONS.source_tmp = common.UnzipTemp(
-        OPTIONS.incremental_source, UNZIP_PATTERN)
-    with zipfile.ZipFile(args[0], 'r') as input_zip, \
-        zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
-      WriteBlockIncrementalOTAPackage(
-          input_zip,
-          source_zip,
-          output_file=args[1])
-
-    if OPTIONS.log_diff:
-      with open(OPTIONS.log_diff, 'w') as out_file:
-        import target_files_diff
-        target_files_diff.recursiveDiff(
-            '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
+    with open(OPTIONS.log_diff, 'w') as out_file:
+      import target_files_diff
+      target_files_diff.recursiveDiff(
+          '', source_dir, target_dir, out_file)
 
   logger.info("done.")
 
diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py
old mode 100755
new mode 100644
diff --git a/tools/releasetools/test_add_img_to_target_files.py b/tools/releasetools/test_add_img_to_target_files.py
index 08e0190..3d0766f 100644
--- a/tools/releasetools/test_add_img_to_target_files.py
+++ b/tools/releasetools/test_add_img_to_target_files.py
@@ -21,7 +21,7 @@
 import common
 import test_utils
 from add_img_to_target_files import (
-    AddCareMapForAbOta, AddPackRadioImages, AppendVBMetaArgsForPartition,
+    AddCareMapForAbOta, AddPackRadioImages,
     CheckAbOtaImages, GetCareMap)
 from rangelib import RangeSet
 
@@ -379,32 +379,6 @@
     # The existing entry should be scheduled to be replaced.
     self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)
 
-  def test_AppendVBMetaArgsForPartition(self):
-    OPTIONS.info_dict = {}
-    cmd = []
-    AppendVBMetaArgsForPartition(cmd, 'system', '/path/to/system.img')
-    self.assertEqual(
-        ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
-
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
-    testdata_dir = test_utils.get_testdata_dir()
-    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
-    OPTIONS.info_dict = {
-        'avb_avbtool': 'avbtool',
-        'avb_vendor_key_path': pubkey,
-        'avb_vendor_rollback_index_location': 5,
-    }
-    cmd = []
-    AppendVBMetaArgsForPartition(cmd, 'vendor', '/path/to/vendor.img')
-    self.assertEqual(2, len(cmd))
-    self.assertEqual('--chain_partition', cmd[0])
-    chained_partition_args = cmd[1].split(':')
-    self.assertEqual(3, len(chained_partition_args))
-    self.assertEqual('vendor', chained_partition_args[0])
-    self.assertEqual('5', chained_partition_args[1])
-    self.assertTrue(os.path.exists(chained_partition_args[2]))
-
   def test_GetCareMap(self):
     sparse_image = test_utils.construct_sparse_image([
         (0xCAC1, 6),
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index bcfb1c1..ceb023f 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1137,6 +1137,30 @@
     }
     self.assertEqual(merged_dict, expected_merged_dict)
 
+  def test_GetAvbPartitionArg(self):
+    info_dict = {}
+    cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
+    self.assertEqual(
+        ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
+
+  @test_utils.SkipIfExternalToolsUnavailable()
+  def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
+    testdata_dir = test_utils.get_testdata_dir()
+    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
+    info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_vendor_key_path': pubkey,
+        'avb_vendor_rollback_index_location': 5,
+    }
+    cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
+    self.assertEqual(2, len(cmd))
+    self.assertEqual('--chain_partition', cmd[0])
+    chained_partition_args = cmd[1].split(':')
+    self.assertEqual(3, len(chained_partition_args))
+    self.assertEqual('vendor', chained_partition_args[0])
+    self.assertEqual('5', chained_partition_args[1])
+    self.assertTrue(os.path.exists(chained_partition_args[2]))
+
 
 class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
   """Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/test_utils.py b/tools/releasetools/test_utils.py
index 1e919f7..2445671 100755
--- a/tools/releasetools/test_utils.py
+++ b/tools/releasetools/test_utils.py
@@ -32,9 +32,12 @@
 logging.basicConfig(stream=sys.stdout)
 
 # Use ANDROID_BUILD_TOP as an indicator to tell if the needed tools (e.g.
-# avbtool, mke2fs) are available while running the tests. Not having the var or
-# having empty string means we can't run the tests that require external tools.
-EXTERNAL_TOOLS_UNAVAILABLE = not os.environ.get("ANDROID_BUILD_TOP")
+# avbtool, mke2fs) are available while running the tests, unless
+# FORCE_RUN_RELEASETOOLS is set to '1'. Not having the required vars means we
+# can't run the tests that require external tools.
+EXTERNAL_TOOLS_UNAVAILABLE = (
+    not os.environ.get('ANDROID_BUILD_TOP') and
+    os.environ.get('FORCE_RUN_RELEASETOOLS') != '1')
 
 
 def SkipIfExternalToolsUnavailable():