Merge "Export BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT to Soong"
diff --git a/core/Makefile b/core/Makefile
index 9471148..5f62234 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -798,10 +798,10 @@
 # $1: boot image target
 define build_boot_board_avb_enabled
   $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
-  $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
+  $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot)))
   $(AVBTOOL) add_hash_footer \
           --image $(1) \
-          --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+          --partition_size $(call get-bootimage-partition-size,$(1),boot) \
           --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \
           $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
 endef
@@ -821,7 +821,7 @@
 define build_boot_supports_boot_signer
   $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
   $(BOOT_SIGNER) /boot $@ $(PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCT_VERITY_SIGNING_KEY).x509.pem $(1)
-  $(call assert-max-image-size,$(1),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+  $(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),boot))
 endef
 
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOT_SIGNER)
@@ -839,7 +839,7 @@
 define build_boot_supports_vboot
   $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1).unsigned
   $(VBOOT_SIGNER) $(FUTILITY) $(1).unsigned $(PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(1).keyblock $(1)
-  $(call assert-max-image-size,$(1),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+  $(call assert-max-image-size,$(1),$(call get-bootimage-partition-size,$(1),boot))
 endef
 
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(VBOOT_SIGNER) $(FUTILITY)
@@ -856,7 +856,7 @@
 # $1: boot image target
 define build_boot_novboot
   $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
-  $(call assert-max-image-size,$1,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+  $(call assert-max-image-size,$1,$(call get-bootimage-partition-size,$(1),boot))
 endef
 
 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
@@ -2384,6 +2384,7 @@
 	  exit 1; \
 	fi
 	ln -sfn $2 $1
+$1: .KATI_SYMLINK_OUTPUTS := $1
 )
 $1
 endef
@@ -4835,7 +4836,7 @@
 # $(1): output file
 # $(2): additional args
 define build-ota-package-target
-PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \
+PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \
     $(OTA_FROM_TARGET_FILES) \
         --verbose \
         --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
@@ -4845,11 +4846,11 @@
         $(BUILT_TARGET_FILES_PACKAGE) $(1)
 endef
 
-name := $(TARGET_PRODUCT)
+product_name := $(TARGET_PRODUCT)
 ifeq ($(TARGET_BUILD_TYPE),debug)
-  name := $(name)_debug
+  product_name := $(product_name)_debug
 endif
-name := $(name)-ota-$(FILE_NAME_TAG)
+name := $(product_name)-ota-$(FILE_NAME_TAG)
 
 INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 INTERNAL_OTA_METADATA := $(PRODUCT_OUT)/ota_metadata
@@ -4864,11 +4865,7 @@
 otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
 
 ifeq ($(BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE),true)
-name := $(TARGET_PRODUCT)
-ifeq ($(TARGET_BUILD_TYPE),debug)
-  name := $(name)_debug
-endif
-name := $(name)-ota-retrofit-$(FILE_NAME_TAG)
+name := $(product_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)
@@ -4885,6 +4882,20 @@
 
 endif # BOARD_BUILD_RETROFIT_DYNAMIC_PARTITIONS_OTA_PACKAGE
 
+ifneq ($(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST),)
+name := $(product_name)-partial-ota-$(FILE_NAME_TAG)
+
+INTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
+$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
+	@echo "Package partial OTA: $@"
+	$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --partial "$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)")
+
+.PHONY: partialotapackage
+partialotapackage: $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET)
+
+endif # BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST
+
 endif    # build_ota_package
 
 # -----------------------------------------------------------------
diff --git a/core/board_config.mk b/core/board_config.mk
index 1ab96ea..457b3bf 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -117,6 +117,7 @@
   BUILD_BROKEN_DUP_RULES \
   BUILD_BROKEN_DUP_SYSPROP \
   BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES \
+  BUILD_BROKEN_ENFORCE_SYSPROP_OWNER \
   BUILD_BROKEN_MISSING_REQUIRED_MODULES \
   BUILD_BROKEN_OUTSIDE_INCLUDE_DIRS \
   BUILD_BROKEN_PREBUILT_ELF_FILES \
@@ -363,6 +364,8 @@
     BUILDING_BOOT_IMAGE :=
   else ifdef BOARD_BOOTIMAGE_PARTITION_SIZE
     BUILDING_BOOT_IMAGE := true
+  else ifneq (,$(foreach kernel,$(BOARD_KERNEL_BINARIES),$(BOARD_$(call to-upper,$(kernel))_BOOTIMAGE_PARTITION_SIZE)))
+    BUILDING_BOOT_IMAGE := true
   endif
 else ifeq ($(PRODUCT_BUILD_BOOT_IMAGE),true)
   ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
diff --git a/core/definitions.mk b/core/definitions.mk
index bfbeee3..daac652 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2610,6 +2610,7 @@
 	@mkdir -p $(dir $$@)
 	@rm -rf $$@
 	$(hide) ln -sf $(2) $$@
+$(3): .KATI_SYMLINK_OUTPUTS := $(3)
 endef
 
 # Copy an apk to a target location while removing classes*.dex
diff --git a/core/main.mk b/core/main.mk
index 40cc70f..9ba43f6 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -895,7 +895,7 @@
 # Scan all modules in general-tests, device-tests and other selected suites and
 # flatten the shared library dependencies.
 define update-host-shared-libs-deps-for-suites
-$(foreach suite,general-tests device-tests vts,\
+$(foreach suite,general-tests device-tests vts art-host-tests,\
   $(foreach m,$(COMPATIBILITY.$(suite).MODULES),\
     $(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\
     $(foreach dep,$(my_deps),\
@@ -1715,6 +1715,7 @@
     $(INTERNAL_UPDATE_PACKAGE_TARGET) \
     $(INTERNAL_OTA_PACKAGE_TARGET) \
     $(INTERNAL_OTA_METADATA) \
+    $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET) \
     $(INTERNAL_OTA_RETROFIT_DYNAMIC_PARTITIONS_PACKAGE_TARGET) \
     $(BUILT_OTATOOLS_PACKAGE) \
     $(SYMBOLS_ZIP) \
diff --git a/core/tasks/art-host-tests.mk b/core/tasks/art-host-tests.mk
index 96e2c74..d771b06 100644
--- a/core/tasks/art-host-tests.mk
+++ b/core/tasks/art-host-tests.mk
@@ -16,12 +16,27 @@
 
 intermediates_dir := $(call intermediates-dir-for,PACKAGING,art-host-tests)
 art_host_tests_zip := $(PRODUCT_OUT)/art-host-tests.zip
-$(art_host_tests_zip) : $(COMPATIBILITY.art-host-tests.FILES) $(SOONG_ZIP)
+# Get the hostside libraries to be packaged in the test zip. Unlike
+# device-tests.mk or general-tests.mk, the files are not copied to the
+# testcases directory.
+my_host_shared_lib_for_art_host_tests := $(foreach f,$(COMPATIBILITY.art-host-tests.HOST_SHARED_LIBRARY.FILES),$(strip \
+    $(eval _cmf_tuple := $(subst :, ,$(f))) \
+    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \
+    $(_cmf_src)))
+
+$(art_host_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_art_host_tests)
+
+$(art_host_tests_zip) : $(COMPATIBILITY.art-host-tests.FILES) $(my_host_shared_lib_for_art_host_tests) $(SOONG_ZIP)
 	echo $(sort $(COMPATIBILITY.art-host-tests.FILES)) | tr " " "\n" > $@.list
 	grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
+	$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+	  echo $$shared_lib >> $@-host-libs.list; \
+	done
 	grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
-	$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list
-	rm -f $@.list $@-host.list $@-target.list
+	$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \
+	  -P target -C $(PRODUCT_OUT) -l $@-target.list \
+	  -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list
+	rm -f $@.list $@-host.list $@-target.list $@-host-libs.list
 
 art-host-tests: $(art_host_tests_zip)
 $(call dist-for-goals, art-host-tests, $(art_host_tests_zip))
diff --git a/core/tasks/boot_jars_package_check.mk b/core/tasks/boot_jars_package_check.mk
index c9a8e27..baa378a 100644
--- a/core/tasks/boot_jars_package_check.mk
+++ b/core/tasks/boot_jars_package_check.mk
@@ -26,7 +26,6 @@
 # (e.g. com.android.media:updatable-media -> updatable-media.com.android.media).
 # Special cases:
 #   - for the "platform" or "system_ext" apex drop the .<apex> suffix
-#   - for the ART apex select release variant
 boot_jars := $(foreach pair,$(PRODUCT_BOOT_JARS) $(PRODUCT_UPDATABLE_BOOT_JARS), \
   $(eval apex := $(call word-colon,1,$(pair))) \
   $(eval jar := $(call word-colon,2,$(pair))) \
@@ -34,7 +33,6 @@
   $(eval sfx := $(q).$(apex)$(q)) \
   $(eval sfx := $(subst $(q).platform$(q),$(q)$(q),$(sfx))) \
   $(eval sfx := $(subst $(q).system_ext$(q),$(q)$(q),$(sfx))) \
-  $(eval sfx := $(subst $(q).com.android.art$(q),$(q).com.android.art.release$(q),$(sfx))) \
   $(eval sfx := $(patsubst $(q)%$(q),%,$(sfx))) \
   $(jar)$(sfx))
 
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index c09daeb..fdd9591 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -16,7 +16,6 @@
 test_suite_tradefed := cts-tradefed
 test_suite_dynamic_config := cts/tools/cts-tradefed/DynamicConfig.xml
 test_suite_readme := cts/tools/cts-tradefed/README
-include_test_suite_notice := true
 
 include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
 
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index f394b96..66f3b5b 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -29,8 +29,7 @@
 test_suite_subdir := android-$(test_suite_name)
 out_dir := $(HOST_OUT)/$(test_suite_name)/$(test_suite_subdir)
 test_artifacts := $(COMPATIBILITY.$(test_suite_name).FILES)
-test_tools := $(HOST_OUT_JAVA_LIBRARIES)/hosttestlib.jar \
-  $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \
+test_tools := $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/tradefed-no-fwk.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/tradefed-test-framework.jar \
   $(HOST_OUT_JAVA_LIBRARIES)/loganalysis.jar \
@@ -80,10 +79,8 @@
          $(HOST_OUT_NOTICE_FILES) $(TARGET_OUT_NOTICE_FILES), \
          $(compatibility_zip_deps)))
 
-ifeq ($(include_test_suite_notice),true)
-  compatibility_zip_deps += $(test_suite_notice_txt)
-  compatibility_zip_resources += $(test_suite_notice_txt)
-endif
+compatibility_zip_deps += $(test_suite_notice_txt)
+compatibility_zip_resources += $(test_suite_notice_txt)
 
 compatibility_zip := $(out_dir).zip
 $(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir)
@@ -112,7 +109,6 @@
 test_suite_readme :=
 test_suite_prebuilt_tools :=
 test_suite_tools :=
-include_test_suite_notice :=
 test_suite_jdk :=
 test_suite_jdk_dir :=
 host_shared_libs :=
diff --git a/target/product/base_product.mk b/target/product/base_product.mk
index 0166ecc..5446064 100644
--- a/target/product/base_product.mk
+++ b/target/product/base_product.mk
@@ -16,6 +16,8 @@
 
 # Base modules and settings for the product partition.
 PRODUCT_PACKAGES += \
+    fs_config_dirs_product \
+    fs_config_files_product \
     group_product \
     ModuleMetadata \
     passwd_product \
diff --git a/target/product/base_system_ext.mk b/target/product/base_system_ext.mk
index 58921d8..852d7ca 100644
--- a/target/product/base_system_ext.mk
+++ b/target/product/base_system_ext.mk
@@ -16,7 +16,9 @@
 
 # Base modules and settings for the system_ext partition.
 PRODUCT_PACKAGES += \
+    fs_config_dirs_system_ext \
+    fs_config_files_system_ext \
     group_system_ext \
-    system_ext_manifest.xml \
     passwd_system_ext \
     selinux_policy_system_ext \
+    system_ext_manifest.xml \
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index f2ef002..f057958 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -18,9 +18,9 @@
 LLNDK: libsync.so
 LLNDK: libvndksupport.so
 LLNDK: libvulkan.so
-VNDK-SP: android.hardware.common-V1-ndk_platform.so
+VNDK-SP: android.hardware.common-V2-ndk_platform.so
 VNDK-SP: android.hardware.common.fmq-V1-ndk_platform.so
-VNDK-SP: android.hardware.graphics.common-V1-ndk_platform.so
+VNDK-SP: android.hardware.graphics.common-V2-ndk_platform.so
 VNDK-SP: android.hardware.graphics.common@1.0.so
 VNDK-SP: android.hardware.graphics.common@1.1.so
 VNDK-SP: android.hardware.graphics.common@1.2.so
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index 5fb68b8..c338462 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -34,8 +34,6 @@
   $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm) \
   $(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),vendor_dlkm) \
   $(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),odm_dlkm) \
-  $(if $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),product) \
-  $(if $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),system_ext) \
 )
 
 ##################################
@@ -45,11 +43,12 @@
 
 LOCAL_MODULE := fs_config_dirs
 LOCAL_REQUIRED_MODULES := \
-	fs_config_dirs_system \
-	$(foreach t,$(fs_config_generate_extra_partition_list),$(LOCAL_MODULE)_$(t))
+  fs_config_dirs_system \
+  fs_config_dirs_system_ext \
+  fs_config_dirs_product \
+  fs_config_dirs_nonsystem
 include $(BUILD_PHONY_PACKAGE)
 
-
 ##################################
 # Generate the <p>/etc/fs_config_files binary files for each partition.
 # Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable.
@@ -58,27 +57,69 @@
 LOCAL_MODULE := fs_config_files
 LOCAL_REQUIRED_MODULES := \
   fs_config_files_system \
-  $(foreach t,$(fs_config_generate_extra_partition_list),$(LOCAL_MODULE)_$(t))
+  fs_config_files_system_ext \
+  fs_config_files_product \
+  fs_config_files_nonsystem
+include $(BUILD_PHONY_PACKAGE)
+
+##################################
+# Generate the system_ext/etc/fs_config_dirs binary file for the target if the
+# system_ext partition is generated. Add fs_config_dirs or fs_config_dirs_system_ext
+# to PRODUCT_PACKAGES in the device make file to enable.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_system_ext
+LOCAL_REQUIRED_MODULES := $(if $(BOARD_USES_SYSTEM_EXTIMAGE)$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),_fs_config_dirs_system_ext)
+include $(BUILD_PHONY_PACKAGE)
+
+##################################
+# Generate the system_ext/etc/fs_config_files binary file for the target if the
+# system_ext partition is generated. Add fs_config_files or fs_config_files_system_ext
+# to PRODUCT_PACKAGES in the device make file to enable.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_system_ext
+LOCAL_REQUIRED_MODULES := $(if $(BOARD_USES_SYSTEM_EXTIMAGE)$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),_fs_config_files_system_ext)
+include $(BUILD_PHONY_PACKAGE)
+
+##################################
+# Generate the product/etc/fs_config_dirs binary file for the target if the
+# product partition is generated. Add fs_config_dirs or fs_config_dirs_product
+# to PRODUCT_PACKAGES in the device make file to enable.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_product
+LOCAL_REQUIRED_MODULES := $(if $(BOARD_USES_PRODUCTIMAGE)$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),_fs_config_dirs_product)
+include $(BUILD_PHONY_PACKAGE)
+
+##################################
+# Generate the product/etc/fs_config_files binary file for the target if the
+# product partition is generated. Add fs_config_files or fs_config_files_product
+# to PRODUCT_PACKAGES in the device make file to enable.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_product
+LOCAL_REQUIRED_MODULES := $(if $(BOARD_USES_PRODUCTIMAGE)$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),_fs_config_files_product)
 include $(BUILD_PHONY_PACKAGE)
 
 ##################################
 # Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions
-# excluding /system. Add fs_config_dirs_nonsystem to PRODUCT_PACKAGES in the
-# device make file to enable.
+# excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to
+# PRODUCT_PACKAGES in the device make file to enable.
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := fs_config_dirs_nonsystem
-LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_dirs_$(t))
+LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),_fs_config_dirs_$(t))
 include $(BUILD_PHONY_PACKAGE)
 
 ##################################
 # Generate the <p>/etc/fs_config_files binary files for all enabled partitions
-# excluding /system. Add fs_config_files_nonsystem to PRODUCT_PACKAGES in the
-# device make file to enable.
+# excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to
+# PRODUCT_PACKAGES in the device make file to enable.
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := fs_config_files_nonsystem
-LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_files_$(t))
+LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),_fs_config_files_$(t))
 include $(BUILD_PHONY_PACKAGE)
 
 ##################################
@@ -134,11 +175,11 @@
 ifneq ($(filter vendor,$(fs_config_generate_extra_partition_list)),)
 ##################################
 # Generate the vendor/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_vendor to PRODUCT_PACKAGES in
-# the device make file to enable.
+# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_vendor
+LOCAL_MODULE := _fs_config_dirs_vendor
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
@@ -158,11 +199,11 @@
 
 ##################################
 # Generate the vendor/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_vendor to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_vendor
+LOCAL_MODULE := _fs_config_files_vendor
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
@@ -185,11 +226,11 @@
 ifneq ($(filter oem,$(fs_config_generate_extra_partition_list)),)
 ##################################
 # Generate the oem/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_oem to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_oem
+LOCAL_MODULE := _fs_config_dirs_oem
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
@@ -209,11 +250,11 @@
 
 ##################################
 # Generate the oem/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_oem to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_oem
+LOCAL_MODULE := _fs_config_files_oem
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
@@ -236,11 +277,11 @@
 ifneq ($(filter odm,$(fs_config_generate_extra_partition_list)),)
 ##################################
 # Generate the odm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_odm to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_odm
+LOCAL_MODULE := _fs_config_dirs_odm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
@@ -260,11 +301,11 @@
 
 ##################################
 # Generate the odm/etc/fs_config_files binary file for the target
-# Add fs_config_files of fs_config_files_odm to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_odm
+LOCAL_MODULE := _fs_config_files_odm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
@@ -287,11 +328,11 @@
 ifneq ($(filter vendor_dlkm,$(fs_config_generate_extra_partition_list)),)
 ##################################
 # Generate the vendor_dlkm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_vendor_dlkm to PRODUCT_PACKAGES in
+# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_vendor_dlkm
+LOCAL_MODULE := _fs_config_dirs_vendor_dlkm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
@@ -311,11 +352,11 @@
 
 ##################################
 # Generate the vendor_dlkm/etc/fs_config_files binary file for the target
-# Add fs_config_files of fs_config_files_vendor_dlkm to PRODUCT_PACKAGES in
+# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_vendor_dlkm
+LOCAL_MODULE := _fs_config_files_vendor_dlkm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
@@ -338,11 +379,11 @@
 ifneq ($(filter odm_dlkm,$(fs_config_generate_extra_partition_list)),)
 ##################################
 # Generate the odm_dlkm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_odm_dlkm to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_odm_dlkm
+LOCAL_MODULE := _fs_config_dirs_odm_dlkm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
@@ -362,11 +403,11 @@
 
 ##################################
 # Generate the odm_dlkm/etc/fs_config_files binary file for the target
-# Add fs_config_files of fs_config_files_odm_dlkm to PRODUCT_PACKAGES in
-# the device make file to enable
+# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
+# in the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_odm_dlkm
+LOCAL_MODULE := _fs_config_files_odm_dlkm
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
@@ -386,14 +427,14 @@
 
 endif
 
-ifneq ($(filter product,$(fs_config_generate_extra_partition_list)),)
+ifneq ($(BOARD_USES_PRODUCTIMAGE)$(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE),)
 ##################################
 # Generate the product/etc/fs_config_dirs binary file for the target
 # Add fs_config_dirs or fs_config_dirs_product to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_product
+LOCAL_MODULE := _fs_config_dirs_product
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc
@@ -413,11 +454,11 @@
 
 ##################################
 # Generate the product/etc/fs_config_files binary file for the target
-# Add fs_config_files of fs_config_files_product to PRODUCT_PACKAGES in
+# Add fs_config_files or fs_config_files_product to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_product
+LOCAL_MODULE := _fs_config_files_product
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc
@@ -436,14 +477,14 @@
 	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
 endif
 
-ifneq ($(filter system_ext,$(fs_config_generate_extra_partition_list)),)
+ifneq ($(BOARD_USES_SYSTEM_EXTIMAGE)$(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE),)
 ##################################
 # Generate the system_ext/etc/fs_config_dirs binary file for the target
 # Add fs_config_dirs or fs_config_dirs_system_ext to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_dirs_system_ext
+LOCAL_MODULE := _fs_config_dirs_system_ext
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
 LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT)/etc
@@ -463,11 +504,11 @@
 
 ##################################
 # Generate the system_ext/etc/fs_config_files binary file for the target
-# Add fs_config_files of fs_config_files_system_ext to PRODUCT_PACKAGES in
+# Add fs_config_files or fs_config_files_system_ext to PRODUCT_PACKAGES in
 # the device make file to enable
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := fs_config_files_system_ext
+LOCAL_MODULE := _fs_config_files_system_ext
 LOCAL_MODULE_CLASS := ETC
 LOCAL_INSTALLED_MODULE_STEM := fs_config_files
 LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT)/etc
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 6f414a5..2da5cc0 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -16,11 +16,15 @@
 #
 """This script merges two partial target files packages.
 
-One package contains framework files, and the other contains vendor files.
-It produces a complete target files package that can be used to generate an
-OTA package.
+One input package contains framework files, and the other contains vendor files.
 
-Usage: merge_target_files.py [args]
+This script produces a complete, merged target files package:
+  - This package can be used to generate a flashable IMG package.
+    See --output-img.
+  - This package can be used to generate an OTA package. See --output-ota.
+  - The merged package is checked for compatibility between the two inputs.
+
+Usage: merge_target_files [args]
 
   --framework-target-files framework-target-files-zip-archive
       The input target files package containing framework bits. This is a zip
@@ -155,16 +159,9 @@
     'SYSTEM/*',
 )
 
-# FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the
-# partial framework target files package that need some special processing, such
-# as some sort of combination with items from the partial vendor target files
-# package.
-
-FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',)
-
 # DEFAULT_FRAMEWORK_MISC_INFO_KEYS is a list of keys to obtain from the
-# framework instance of META/misc_info.txt. The remaining keys from the
-# vendor instance.
+# framework instance of META/misc_info.txt. The remaining keys should come
+# from the vendor instance.
 
 DEFAULT_FRAMEWORK_MISC_INFO_KEYS = (
     'avb_system_hashtree_enable',
@@ -205,13 +202,6 @@
     'VENDOR/*',
 )
 
-# VENDOR_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the
-# partial vendor target files package that need some special processing, such as
-# some sort of combination with items from the partial framework target files
-# package.
-
-VENDOR_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',)
-
 # The merge config lists should not attempt to extract items from both
 # builds for any of the following partitions. The partitions in
 # SINGLE_BUILD_PARTITIONS should come entirely from a single build (either
@@ -320,8 +310,8 @@
     framework_item_list: The list of items to extract from the partial framework
       target files package as is.
     framework_misc_info_keys: A list of keys to obtain from the framework
-      instance of META/misc_info.txt. The remaining keys from the vendor
-      instance.
+      instance of META/misc_info.txt. The remaining keys should come from the
+      vendor instance.
     vendor_item_list: The list of items to extract from the partial vendor
       target files package as is.
 
@@ -346,10 +336,15 @@
                  'this script.')
     has_error = True
 
+  # Check that partitions only come from one input.
   for partition in SINGLE_BUILD_PARTITIONS:
-    in_framework = any(
-        item.startswith(partition) for item in framework_item_list)
-    in_vendor = any(item.startswith(partition) for item in vendor_item_list)
+    image_path = 'IMAGES/{}.img'.format(partition.lower().replace('/', ''))
+    in_framework = (
+        any(item.startswith(partition) for item in framework_item_list) or
+        image_path in framework_item_list)
+    in_vendor = (
+        any(item.startswith(partition) for item in vendor_item_list) or
+        image_path in vendor_item_list)
     if in_framework and in_vendor:
       logger.error(
           'Cannot extract items from %s for both the framework and vendor'
@@ -375,8 +370,8 @@
   framework directory and the vendor directory, placing the merged result in the
   output directory. The precondition in that the files are already extracted.
   The post condition is that the output META/ab_partitions.txt contains the
-  merged content. The format for each ab_partitions.txt a one partition name per
-  line. The output file contains the union of the parition names.
+  merged content. The format for each ab_partitions.txt is one partition name
+  per line. The output file contains the union of the partition names.
 
   Args:
     framework_target_files_temp_dir: The name of a directory containing the
@@ -429,8 +424,8 @@
       create the output target files package after all the special cases are
       processed.
     framework_misc_info_keys: A list of keys to obtain from the framework
-      instance of META/misc_info.txt. The remaining keys from the vendor
-      instance.
+      instance of META/misc_info.txt. The remaining keys should come from the
+      vendor instance.
   """
 
   misc_info_path = ['META', 'misc_info.txt']
@@ -674,8 +669,8 @@
       create the output target files package after all the special cases are
       processed.
     framework_misc_info_keys: A list of keys to obtain from the framework
-      instance of META/misc_info.txt. The remaining keys from the vendor
-      instance.
+      instance of META/misc_info.txt. The remaining keys should come from the
+      vendor instance.
     framework_partition_set: Partitions that are considered framework
       partitions. Used to filter apexkeys.txt and apkcerts.txt.
     vendor_partition_set: Partitions that are considered vendor partitions. Used
@@ -721,26 +716,6 @@
       file_name='apexkeys.txt')
 
 
-def files_from_path(target_path, extra_args=None):
-  """Gets files under given path.
-
-  Get (sub)files from given target path and return sorted list.
-
-  Args:
-    target_path: Target path to get subfiles.
-    extra_args: List of extra argument for find command. Optional.
-
-  Returns:
-    Sorted files and directories list.
-  """
-
-  find_command = ['find', target_path] + (extra_args or [])
-  find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
-  return common.RunAndCheckOutput(['sort'],
-                                  stdin=find_process.stdout,
-                                  verbose=False)
-
-
 def create_merged_package(temp_dir, framework_target_files, framework_item_list,
                           vendor_target_files, vendor_item_list,
                           framework_misc_info_keys, rebuild_recovery):
@@ -762,64 +737,42 @@
       target files package as is, meaning these items will land in the output
       target files package exactly as they appear in the input partial vendor
       target files package.
-    framework_misc_info_keys: The list of keys to obtain from the framework
-      instance of META/misc_info.txt. The remaining keys from the vendor
-      instance.
+    framework_misc_info_keys: A list of keys to obtain from the framework
+      instance of META/misc_info.txt. The remaining keys should come from the
+      vendor instance.
     rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
       devices and write it to the system image.
 
   Returns:
     Path to merged package under temp directory.
   """
+  # Extract "as is" items from the input framework and vendor partial target
+  # files packages directly into the output temporary directory, since these items
+  # do not need special case processing.
 
-  # Create directory names that we'll use when we extract files from framework,
-  # and vendor, and for zipping the final output.
-
-  framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
-  vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
   output_target_files_temp_dir = os.path.join(temp_dir, 'output')
-
-  # Extract "as is" items from the input framework partial target files package.
-  # We extract them directly into the output temporary directory since the
-  # items do not need special case processing.
-
   extract_items(
       target_files=framework_target_files,
       target_files_temp_dir=output_target_files_temp_dir,
       extract_item_list=framework_item_list)
-
-  # Extract "as is" items from the input vendor partial target files package. We
-  # extract them directly into the output temporary directory since the items
-  # do not need special case processing.
-
   extract_items(
       target_files=vendor_target_files,
       target_files_temp_dir=output_target_files_temp_dir,
       extract_item_list=vendor_item_list)
 
-  # Extract "special" items from the input framework partial target files
-  # package. We extract these items to different directory since they require
-  # special processing before they will end up in the output directory.
-
+  # Perform special case processing on META/* items.
+  # After this function completes successfully, all the files we need to create
+  # the output target files package are in place.
+  framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
+  vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
   extract_items(
       target_files=framework_target_files,
       target_files_temp_dir=framework_target_files_temp_dir,
-      extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST)
-
-  # Extract "special" items from the input vendor partial target files package.
-  # We extract these items to different directory since they require special
-  # processing before they will end up in the output directory.
-
+      extract_item_list=('META/*',))
   extract_items(
       target_files=vendor_target_files,
       target_files_temp_dir=vendor_target_files_temp_dir,
-      extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST)
-
-  # Now that the temporary directories contain all the extracted files, perform
-  # special case processing on any items that need it. After this function
-  # completes successfully, all the files we need to create the output target
-  # files package are in place.
-
+      extract_item_list=('META/*',))
   process_special_cases(
       framework_target_files_temp_dir=framework_target_files_temp_dir,
       vendor_target_files_temp_dir=vendor_target_files_temp_dir,
@@ -845,8 +798,10 @@
 
   # Regenerate IMAGES in the target directory.
 
-  add_img_args = ['--verbose']
-  add_img_args.append('--add_missing')
+  add_img_args = [
+      '--verbose',
+      '--add_missing',
+  ]
   # TODO(b/132730255): Remove this if statement.
   if rebuild_recovery:
     add_img_args.append('--rebuild_recovery')
@@ -899,6 +854,15 @@
   output_zip = os.path.abspath(output_file)
   output_target_files_meta_dir = os.path.join(source_dir, 'META')
 
+  def files_from_path(target_path, extra_args=None):
+    """Gets files under the given path and return a sorted list."""
+    find_command = ['find', target_path] + (extra_args or [])
+    find_process = common.Run(
+        find_command, stdout=subprocess.PIPE, verbose=False)
+    return common.RunAndCheckOutput(['sort'],
+                                    stdin=find_process.stdout,
+                                    verbose=False)
+
   meta_content = files_from_path(output_target_files_meta_dir)
   other_content = files_from_path(
       source_dir,
@@ -947,9 +911,9 @@
       target files package as is, meaning these items will land in the output
       target files package exactly as they appear in the input partial framework
       target files package.
-    framework_misc_info_keys: The list of keys to obtain from the framework
-      instance of META/misc_info.txt. The remaining keys from the vendor
-      instance.
+    framework_misc_info_keys: A list of keys to obtain from the framework
+      instance of META/misc_info.txt. The remaining keys should come from the
+      vendor instance.
     vendor_target_files: The name of the zip archive containing the vendor
       partial target files package.
     vendor_item_list: The list of items to extract from the partial vendor
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 7dc648f..fba43e9 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -85,6 +85,13 @@
       If not set, generates A/B package for A/B device and non-A/B package for
       non-A/B device.
 
+  -o  (--oem_settings) <main_file[,additional_files...]>
+      Comma separated list of files used to specify the expected OEM-specific
+      properties on the OEM partition of the intended device. Multiple expected
+      values can be used by providing multiple files. Only the first dict will
+      be used to compute fingerprint, while the rest will be used to assert
+      OEM-specific properties.
+
 Non-A/B OTA specific options
 
   -b  (--binary) <file>
@@ -114,13 +121,6 @@
       builds for an incremental package. This option is only meaningful when -i
       is specified.
 
-  -o  (--oem_settings) <main_file[,additional_files...]>
-      Comma seperated list of files used to specify the expected OEM-specific
-      properties on the OEM partition of the intended device. Multiple expected
-      values can be used by providing multiple files. Only the first dict will
-      be used to compute fingerprint, while the rest will be used to assert
-      OEM-specific properties.
-
   --oem_no_mount
       For devices with OEM-specific properties but without an OEM partition, do
       not mount the OEM partition in the updater-script. This should be very
@@ -206,6 +206,11 @@
   --partial "<PARTITION> [<PARTITION>[...]]"
       Generate partial updates, overriding ab_partitions list with the given
       list.
+
+  --custom_image <custom_partition=custom_image>
+      Use the specified custom_image to update custom_partition when generating
+      an A/B OTA package. e.g. "--custom_image oem=oem.img --custom_image
+      cus=cus_test.img"
 """
 
 from __future__ import print_function
@@ -262,7 +267,7 @@
 OPTIONS.skip_compatibility_check = False
 OPTIONS.disable_fec_computation = False
 OPTIONS.partial = None
-
+OPTIONS.custom_images = {}
 
 POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
 DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
@@ -901,6 +906,43 @@
 
   return target_file
 
+def GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images):
+  """Returns a target-files.zip for custom partitions update.
+
+  This function modifies ab_partitions list with the desired custom partitions
+  and puts the custom images into the target target-files.zip.
+
+  Args:
+    input_file: The input target-files.zip filename.
+    custom_images: A map of custom partitions and custom images.
+
+  Returns:
+    The filename of a target-files.zip which has renamed the custom images in
+    the IMAGS/ to their partition names.
+  """
+  # Use zip2zip to avoid extracting the zipfile.
+  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
+  cmd = ['zip2zip', '-i', input_file, '-o', target_file]
+
+  with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
+    namelist = input_zip.namelist()
+
+  # Write {custom_image}.img as {custom_partition}.img.
+  for custom_partition, custom_image in custom_images.items():
+    default_custom_image = '{}.img'.format(custom_partition)
+    if default_custom_image != custom_image:
+      logger.info("Update custom partition '%s' with '%s'",
+                  custom_partition, custom_image)
+      # Default custom image need to be deleted first.
+      namelist.remove('IMAGES/{}'.format(default_custom_image))
+      # IMAGES/{custom_image}.img:IMAGES/{custom_partition}.img.
+      cmd.extend(['IMAGES/{}:IMAGES/{}'.format(custom_image,
+                                               default_custom_image)])
+
+  cmd.extend(['{}:{}'.format(name, name) for name in namelist])
+  common.RunAndCheckOutput(cmd)
+
+  return target_file
 
 def GenerateAbOtaPackage(target_file, output_file, source_file=None):
   """Generates an Android OTA package that has A/B update payload."""
@@ -927,6 +969,11 @@
 
   additional_args = []
 
+  # Prepare custom images.
+  if OPTIONS.custom_images:
+    target_file = GetTargetFilesZipForCustomImagesUpdates(
+        target_file, OPTIONS.custom_images)
+
   if OPTIONS.retrofit_dynamic_partitions:
     target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
         target_file, target_info.get("super_block_devices").strip().split(),
@@ -1105,6 +1152,9 @@
       if not partitions:
         raise ValueError("Cannot parse partitions in {}".format(a))
       OPTIONS.partial = partitions
+    elif o == "--custom_image":
+      custom_partition, custom_image = a.split("=")
+      OPTIONS.custom_images[custom_partition] = custom_image
     else:
       return False
     return True
@@ -1144,6 +1194,7 @@
                                  "force_non_ab",
                                  "boot_variable_file=",
                                  "partial=",
+                                 "custom_image=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py
index ff8593b..7ea7f96 100644
--- a/tools/releasetools/test_merge_target_files.py
+++ b/tools/releasetools/test_merge_target_files.py
@@ -117,6 +117,15 @@
                               DEFAULT_FRAMEWORK_MISC_INFO_KEYS,
                               vendor_item_list))
 
+  def test_validate_config_lists_ReturnsFalseIfSharedExtractedPartitionImage(
+      self):
+    vendor_item_list = list(DEFAULT_VENDOR_ITEM_LIST)
+    vendor_item_list.append('IMAGES/system.img')
+    self.assertFalse(
+        validate_config_lists(DEFAULT_FRAMEWORK_ITEM_LIST,
+                              DEFAULT_FRAMEWORK_MISC_INFO_KEYS,
+                              vendor_item_list))
+
   def test_validate_config_lists_ReturnsFalseIfBadSystemMiscInfoKeys(self):
     for bad_key in ['dynamic_partition_list', 'super_partition_groups']:
       framework_misc_info_keys = list(DEFAULT_FRAMEWORK_MISC_INFO_KEYS)
@@ -144,8 +153,7 @@
 
     process_apex_keys_apk_certs_common(framework_dir, vendor_dir, output_dir,
                                        set(['product', 'system', 'system_ext']),
-                                       set(['odm', 'vendor']),
-                                       'apexkeys.txt')
+                                       set(['odm', 'vendor']), 'apexkeys.txt')
 
     merged_entries = []
     merged_path = os.path.join(self.testdata_dir, 'apexkeys_merge.txt')
@@ -180,8 +188,7 @@
     self.assertRaises(ValueError, process_apex_keys_apk_certs_common,
                       framework_dir, conflict_dir, output_dir,
                       set(['product', 'system', 'system_ext']),
-                      set(['odm', 'vendor']),
-                      'apexkeys.txt')
+                      set(['odm', 'vendor']), 'apexkeys.txt')
 
   def test_process_apex_keys_apk_certs_HandlesApkCertsSyntax(self):
     output_dir = common.MakeTempDir()
@@ -201,8 +208,7 @@
 
     process_apex_keys_apk_certs_common(framework_dir, vendor_dir, output_dir,
                                        set(['product', 'system', 'system_ext']),
-                                       set(['odm', 'vendor']),
-                                       'apkcerts.txt')
+                                       set(['odm', 'vendor']), 'apkcerts.txt')
 
     merged_entries = []
     merged_path = os.path.join(self.testdata_dir, 'apkcerts_merge.txt')
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index 9752c2b..8bf7778 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -27,6 +27,7 @@
     FinalizeMetadata, GetPackageMetadata, PropertyFiles)
 from ota_from_target_files import (
     _LoadOemDicts, AbOtaPropertyFiles,
+    GetTargetFilesZipForCustomImagesUpdates,
     GetTargetFilesZipForPartialUpdates,
     GetTargetFilesZipForSecondaryImages,
     GetTargetFilesZipWithoutPostinstallConfig,
@@ -545,6 +546,46 @@
     with zipfile.ZipFile(target_file) as verify_zip:
       self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
 
+  @test_utils.SkipIfExternalToolsUnavailable()
+  def test_GetTargetFilesZipForCustomImagesUpdates_oemDefaultImage(self):
+    input_file = construct_target_files()
+    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:
+      common.ZipWriteStr(append_zip, 'IMAGES/oem.img', 'oem')
+      common.ZipWriteStr(append_zip, 'IMAGES/oem_test.img', 'oem_test')
+
+    target_file = GetTargetFilesZipForCustomImagesUpdates(
+        input_file, {'oem': 'oem.img'})
+
+    with zipfile.ZipFile(target_file) as verify_zip:
+      namelist = verify_zip.namelist()
+      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()
+      oem_image = verify_zip.read('IMAGES/oem.img').decode()
+
+    self.assertIn('META/ab_partitions.txt', namelist)
+    self.assertEqual('boot\nsystem\nvendor\nbootloader\nmodem', ab_partitions)
+    self.assertIn('IMAGES/oem.img', namelist)
+    self.assertEqual('oem', oem_image)
+
+  @test_utils.SkipIfExternalToolsUnavailable()
+  def test_GetTargetFilesZipForCustomImagesUpdates_oemTestImage(self):
+    input_file = construct_target_files()
+    with zipfile.ZipFile(input_file, 'a', allowZip64=True) as append_zip:
+      common.ZipWriteStr(append_zip, 'IMAGES/oem.img', 'oem')
+      common.ZipWriteStr(append_zip, 'IMAGES/oem_test.img', 'oem_test')
+
+    target_file = GetTargetFilesZipForCustomImagesUpdates(
+        input_file, {'oem': 'oem_test.img'})
+
+    with zipfile.ZipFile(target_file) as verify_zip:
+      namelist = verify_zip.namelist()
+      ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()
+      oem_image = verify_zip.read('IMAGES/oem.img').decode()
+
+    self.assertIn('META/ab_partitions.txt', namelist)
+    self.assertEqual('boot\nsystem\nvendor\nbootloader\nmodem', ab_partitions)
+    self.assertIn('IMAGES/oem.img', namelist)
+    self.assertEqual('oem_test', oem_image)
+
   def _test_FinalizeMetadata(self, large_entry=False):
     entries = [
         'required-entry1',