Merge "Add an option to input the boot variables for OTA package generation"
diff --git a/core/Makefile b/core/Makefile
index 6926b26..35d744a 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -2830,6 +2830,41 @@
 endef
 
 # -----------------------------------------------------------------
+# custom images
+INSTALLED_CUSTOMIMAGES_TARGET :=
+
+ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
+INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS :=
+
+# Sign custom image.
+# $(1): the prebuilt custom image.
+# $(2): the mount point of the prebuilt custom image.
+# $(3): the signed custom image target.
+define sign_custom_image
+$(3): $(1) $(INTERNAL_USERIMAGES_DEPS)
+	@echo Target custom image: $(3)
+	mkdir -p $(dir $(3))
+	cp $(1) $(3)
+ifeq ($(BOARD_AVB_ENABLE),true)
+	PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$$$PATH \
+          $(AVBTOOL) add_hashtree_footer \
+          --image $(3) \
+          --key $(BOARD_AVB_$(call to-upper,$(2))_KEY_PATH) \
+          --algorithm $(BOARD_AVB_$(call to-upper,$(2))_ALGORITHM) \
+          --partition_size $(BOARD_AVB_$(call to-upper,$(2))_PARTITION_SIZE) \
+          --partition_name $(2) \
+          $(INTERNAL_AVB_CUSTOMIMAGES_SIGNING_ARGS) \
+          $(BOARD_AVB_$(call to-upper,$(2))_ADD_HASHTREE_FOOTER_ARGS)
+endif
+INSTALLED_CUSTOMIMAGES_TARGET += $(3)
+endef
+
+$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+  $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST), \
+     $(eval $(call sign_custom_image,$(image),$(partition),$(PRODUCT_OUT)/$(notdir $(image))))))
+endif
+
+# -----------------------------------------------------------------
 # vbmeta image
 ifeq ($(BOARD_AVB_ENABLE),true)
 
@@ -3000,6 +3035,18 @@
             --include_descriptors_from_image $(call images-for-partitions,$(1)))))
 endef
 
+# Checks and sets build variables for a custom chained partition to include it into vbmeta.img.
+# $(1): the custom partition to enable AVB chain.
+define check-and-set-custom-avb-chain-args
+$(eval part := $(1))
+$(eval PART=$(call to-upper,$(part)))
+$(eval _rollback_index_location := BOARD_AVB_$(PART)_ROLLBACK_INDEX_LOCATION)
+$(if $($(_rollback_index_location)),,$(error $(_rollback_index_location) is not defined))
+
+INTERNAL_AVB_MAKE_VBMETA_IMAGE_ARGS += \
+    --chain_partition $(part):$($(_rollback_index_location)):$(AVB_CHAIN_KEY_DIR)/$(part).avbpubkey
+endef
+
 ifdef INSTALLED_BOOTIMAGE_TARGET
 $(eval $(call check-and-set-avb-args,boot))
 endif
@@ -3043,6 +3090,11 @@
 $(eval $(call check-and-set-avb-args,vbmeta_vendor))
 endif
 
+ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
+$(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+    $(eval $(call check-and-set-custom-avb-chain-args,$(partition))))
+endif
+
 # Add kernel cmdline descriptor for kernel to mount system.img as root with
 # dm-verity. This works when system.img is either chained or not-chained:
 # - chained: The --setup_as_rootfs_from_kernel option will add dm-verity kernel
@@ -3113,6 +3165,10 @@
   $(if $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH),\
     $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VBMETA_VENDOR_KEY_PATH) \
         --output $(1)/vbmeta_vendor.avbpubkey)
+  $(if $(BOARD_CUSTOMIMAGES_PARTITION_LIST),\
+    $(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+        $(AVBTOOL) extract_public_key --key $(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH) \
+            --output $(1)/$(partition).avbpubkey;))
 endef
 
 # Builds a chained VBMeta image. This VBMeta image will contain the descriptors for the partitions
@@ -3180,6 +3236,7 @@
 	    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \
 	    $(INSTALLED_ODMIMAGE_TARGET) \
 	    $(INSTALLED_DTBOIMAGE_TARGET) \
+	    $(INSTALLED_CUSTOMIMAGES_TARGET) \
 	    $(INSTALLED_RECOVERYIMAGE_TARGET) \
 	    $(INSTALLED_VBMETA_SYSTEMIMAGE_TARGET) \
 	    $(INSTALLED_VBMETA_VENDORIMAGE_TARGET) \
@@ -3739,6 +3796,16 @@
 	$(hide) echo "avb_recovery_algorithm=$(BOARD_AVB_RECOVERY_ALGORITHM)" >> $@
 	$(hide) echo "avb_recovery_rollback_index_location=$(BOARD_AVB_RECOVERY_ROLLBACK_INDEX_LOCATION)" >> $@
 endif # BOARD_AVB_RECOVERY_KEY_PATH
+ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))
+	$(hide) echo "avb_custom_images_partition_list=$(BOARD_CUSTOMIMAGES_PARTITION_LIST)" >> $@
+	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+	    echo "avb_$(partition)_key_path=$(BOARD_AVB_$(call to-upper,$(partition))_KEY_PATH)"  >> $@; \
+	    echo "avb_$(partition)_algorithm=$(BOARD_AVB_$(call to-upper,$(partition))_ALGORITHM)"  >> $@; \
+	    echo "avb_$(partition)_add_hashtree_footer_args=$(BOARD_AVB_$(call to-upper,$(partition))_ADD_HASHTREE_FOOTER_ARGS)"  >> $@; \
+	    echo "avb_$(partition)_rollback_index_location=$(BOARD_AVB_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)"  >> $@; \
+	    echo "avb_$(partition)_partition_size=$(BOARD_AVB_$(call to-upper,$(partition))_PARTITION_SIZE)"  >> $@; \
+	    echo "avb_$(partition)_image_list=$(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),$(notdir $(image)))" >> $@;)
+endif # BOARD_CUSTOMIMAGES_PARTITION_LIST
 ifneq (,$(strip $(BOARD_AVB_VBMETA_SYSTEM)))
 	$(hide) echo "avb_vbmeta_system=$(BOARD_AVB_VBMETA_SYSTEM)" >> $@
 	$(hide) echo "avb_vbmeta_system_args=$(BOARD_AVB_MAKE_VBMETA_SYSTEM_IMAGE_ARGS)" >> $@
@@ -3972,6 +4039,7 @@
 	    $(INSTALLED_VBMETAIMAGE_TARGET) \
 	    $(INSTALLED_ODMIMAGE_TARGET) \
 	    $(INSTALLED_DTBOIMAGE_TARGET) \
+	    $(INSTALLED_CUSTOMIMAGES_TARGET) \
 	    $(INTERNAL_SYSTEMOTHERIMAGE_FILES) \
 	    $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
 	    $(INSTALLED_KERNEL_TARGET) \
@@ -4216,6 +4284,11 @@
 	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
 	$(hide) cp $(INSTALLED_DTBOIMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
 endif # BOARD_PREBUILT_DTBOIMAGE
+ifneq ($(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)),)
+	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
+	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+	    $(foreach image,$(BOARD_AVB_$(call to-upper,$(partition))_IMAGE_LIST),cp $(image) $(zip_root)/PREBUILT_IMAGES/;))
+endif # BOARD_CUSTOMIMAGES_PARTITION_LIST
 	@# The radio images in BOARD_PACK_RADIOIMAGES will be additionally copied from RADIO/ into
 	@# IMAGES/, which then will be added into <product>-img.zip. Such images must be listed in
 	@# INSTALLED_RADIOIMAGE_TARGET.
diff --git a/envsetup.sh b/envsetup.sh
index 8699a19..0ec7e6f 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -51,7 +51,8 @@
 }
 
 # Get all the build variables needed by this script in a single call to the build system.
-function build_build_var_cache() {
+function build_build_var_cache()
+{
     local T=$(gettop)
     # Grep out the variable names from the script.
     cached_vars=(`cat $T/build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`)
@@ -63,7 +64,8 @@
                         --var-prefix=var_cache_ \
                         --abs-var-prefix=abs_var_cache_`
     local ret=$?
-    if [ $ret -ne 0 ]; then
+    if [ $ret -ne 0 ]
+    then
         unset build_dicts_script
         return $ret
     fi
@@ -71,7 +73,8 @@
     eval "$build_dicts_script"
     ret=$?
     unset build_dicts_script
-    if [ $ret -ne 0 ]; then
+    if [ $ret -ne 0 ]
+    then
         return $ret
     fi
     BUILD_VAR_CACHE_READY="true"
@@ -79,7 +82,8 @@
 
 # Delete the build var cache, so that we can still call into the build system
 # to get build variables not listed in this script.
-function destroy_build_var_cache() {
+function destroy_build_var_cache()
+{
     unset BUILD_VAR_CACHE_READY
     local v
     for v in $cached_vars; do
@@ -93,10 +97,12 @@
 }
 
 # Get the value of a build variable as an absolute path.
-function get_abs_build_var() {
-    if [ "$BUILD_VAR_CACHE_READY" == "true" ]; then
+function get_abs_build_var()
+{
+    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
+    then
         eval "echo \"\${abs_var_cache_$1}\""
-        return
+    return
     fi
 
     local T=$(gettop)
@@ -108,8 +114,10 @@
 }
 
 # Get the exact value of a build variable.
-function get_build_var() {
-    if [ "$BUILD_VAR_CACHE_READY" == "true" ]; then
+function get_build_var()
+{
+    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
+    then
         eval "echo \"\${var_cache_$1}\""
         return 0
     fi
@@ -123,7 +131,8 @@
 }
 
 # check to see if the supplied product is one we can build
-function check_product() {
+function check_product()
+{
     local T=$(gettop)
     if [ ! "$T" ]; then
         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
@@ -140,18 +149,21 @@
 VARIANT_CHOICES=(user userdebug eng)
 
 # check to see if the supplied variant is valid
-function check_variant() {
+function check_variant()
+{
     local v
     for v in ${VARIANT_CHOICES[@]}
     do
-        if [ "$v" == "$1" ]; then
+        if [ "$v" = "$1" ]
+        then
             return 0
         fi
     done
     return 1
 }
 
-function setpaths() {
+function setpaths()
+{
     local T=$(gettop)
     if [ ! "$T" ]; then
         echo "Couldn't locate the top of the tree.  Try setting TOP."
@@ -173,10 +185,10 @@
     # due to "C:\Program Files" being in the path.
 
     # out with the old
-    if [ -n "$ANDROID_BUILD_PATHS" ]; then
+    if [ -n "$ANDROID_BUILD_PATHS" ] ; then
         export PATH=${PATH/$ANDROID_BUILD_PATHS/}
     fi
-    if [ -n "$ANDROID_PRE_BUILD_PATHS" ]; then
+    if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then
         export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/}
         # strip leading ':', if any
         export PATH=${PATH/:%/}
@@ -197,18 +209,14 @@
     local ARCH=$(get_build_var TARGET_ARCH)
     local toolchaindir toolchaindir2=
     case $ARCH in
-        x86)
-            toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
+        x86) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
             ;;
-        x86_64)
-            toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
+        x86_64) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin
             ;;
-        arm)
-            toolchaindir=arm/arm-linux-androideabi-$targetgccversion/bin
+        arm) toolchaindir=arm/arm-linux-androideabi-$targetgccversion/bin
             ;;
-        arm64)
-            toolchaindir=aarch64/aarch64-linux-android-$targetgccversion/bin
-            toolchaindir2=arm/arm-linux-androideabi-$targetgccversion2/bin
+        arm64) toolchaindir=aarch64/aarch64-linux-android-$targetgccversion/bin;
+               toolchaindir2=arm/arm-linux-androideabi-$targetgccversion2/bin
             ;;
         *)
             echo "Can't find toolchain for unknown architecture: $ARCH"
@@ -309,7 +317,8 @@
     #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
 }
 
-function printconfig() {
+function printconfig()
+{
     local T=$(gettop)
     if [ ! "$T" ]; then
         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
@@ -318,7 +327,8 @@
     get_build_var report_config
 }
 
-function set_stuff_for_environment() {
+function set_stuff_for_environment()
+{
     setpaths
     set_sequence_number
 
@@ -327,7 +337,8 @@
     export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
 }
 
-function set_sequence_number() {
+function set_sequence_number()
+{
     export BUILD_ENV_SEQUENCE_NUMBER=13
 }
 
@@ -342,7 +353,8 @@
     return 0
 }
 
-function addcompletions() {
+function addcompletions()
+{
     local T dir f
 
     # Keep us from trying to run in something that's neither bash nor zsh.
@@ -370,7 +382,7 @@
         fi
     done
 
-    if should_add_completion bit; then
+    if should_add_completion bit ; then
         complete -C "bit --tab" bit
     fi
     if [ -z "$ZSH_VERSION" ]; then
@@ -383,7 +395,8 @@
     complete -F _complete_android_module_names m
 }
 
-function choosetype() {
+function choosetype()
+{
     echo "Build type choices are:"
     echo "     1. release"
     echo "     2. debug"
@@ -398,7 +411,7 @@
     while [ -z $TARGET_BUILD_TYPE ]
     do
         echo -n "Which would you like? ["$DEFAULT_NUM"] "
-        if [ -z "$1" ]; then
+        if [ -z "$1" ] ; then
             read ANSWER
         else
             echo $1
@@ -426,7 +439,7 @@
             echo
             ;;
         esac
-        if [ -n "$1" ]; then
+        if [ -n "$1" ] ; then
             break
         fi
     done
@@ -442,9 +455,10 @@
 # that kinda works with a generic product, but really, you should
 # pick a product by name.
 #
-function chooseproduct() {
+function chooseproduct()
+{
     local default_value
-    if [ "x$TARGET_PRODUCT" != x ]; then
+    if [ "x$TARGET_PRODUCT" != x ] ; then
         default_value=$TARGET_PRODUCT
     else
         default_value=aosp_arm
@@ -456,23 +470,24 @@
     while [ -z "$TARGET_PRODUCT" ]
     do
         echo -n "Which product would you like? [$default_value] "
-        if [ -z "$1" ]; then
+        if [ -z "$1" ] ; then
             read ANSWER
         else
             echo $1
             ANSWER=$1
         fi
 
-        if [ -z "$ANSWER" ]; then
+        if [ -z "$ANSWER" ] ; then
             export TARGET_PRODUCT=$default_value
         else
-            if check_product $ANSWER; then
+            if check_product $ANSWER
+            then
                 export TARGET_PRODUCT=$ANSWER
             else
                 echo "** Not a valid product: $ANSWER"
             fi
         fi
-        if [ -n "$1" ]; then
+        if [ -n "$1" ] ; then
             break
         fi
     done
@@ -482,7 +497,8 @@
     destroy_build_var_cache
 }
 
-function choosevariant() {
+function choosevariant()
+{
     echo "Variant choices are:"
     local index=1
     local v
@@ -501,33 +517,35 @@
     while [ -z "$TARGET_BUILD_VARIANT" ]
     do
         echo -n "Which would you like? [$default_value] "
-        if [ -z "$1" ]; then
+        if [ -z "$1" ] ; then
             read ANSWER
         else
             echo $1
             ANSWER=$1
         fi
 
-        if [ -z "$ANSWER" ]; then
+        if [ -z "$ANSWER" ] ; then
             export TARGET_BUILD_VARIANT=$default_value
-        elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$"); then
-            if [ "$ANSWER" -le "${#VARIANT_CHOICES[@]}" ]; then
+        elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then
+            if [ "$ANSWER" -le "${#VARIANT_CHOICES[@]}" ] ; then
                 export TARGET_BUILD_VARIANT=${VARIANT_CHOICES[@]:$(($ANSWER-1)):1}
             fi
         else
-            if check_variant $ANSWER; then
+            if check_variant $ANSWER
+            then
                 export TARGET_BUILD_VARIANT=$ANSWER
             else
                 echo "** Not a valid variant: $ANSWER"
             fi
         fi
-        if [ -n "$1" ]; then
+        if [ -n "$1" ] ; then
             break
         fi
     done
 }
 
-function choosecombo() {
+function choosecombo()
+{
     choosetype $1
 
     echo
@@ -545,7 +563,8 @@
     destroy_build_var_cache
 }
 
-function add_lunch_combo() {
+function add_lunch_combo()
+{
     if [ -n "$ZSH_VERSION" ]; then
         echo -n "${funcfiletrace[1]}: "
     else
@@ -554,7 +573,8 @@
     echo "add_lunch_combo is obsolete. Use COMMON_LUNCH_CHOICES in your AndroidProducts.mk instead."
 }
 
-function print_lunch_menu() {
+function print_lunch_menu()
+{
     local uname=$(uname)
     local choices
     choices=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null)
@@ -564,7 +584,8 @@
     echo "You're building on" $uname
     echo
 
-    if [ $ret -ne 0 ]; then
+    if [ $ret -ne 0 ]
+    then
         echo "Warning: Cannot display lunch menu."
         echo
         echo "Note: You can invoke lunch with an explicit target:"
@@ -587,7 +608,8 @@
     echo
 }
 
-function lunch() {
+function lunch()
+{
     local answer
 
     if [[ $# -gt 1 ]]; then
@@ -605,13 +627,17 @@
 
     local selection=
 
-    if [ -z "$answer" ]; then
+    if [ -z "$answer" ]
+    then
         selection=aosp_arm-eng
-    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$"); then
+    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
+    then
         local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
-        if [ $answer -le ${#choices[@]} ]; then
+        if [ $answer -le ${#choices[@]} ]
+        then
             # array in zsh starts from 1 instead of 0.
-            if [ -n "$ZSH_VERSION" ]; then
+            if [ -n "$ZSH_VERSION" ]
+            then
                 selection=${choices[$(($answer))]}
             else
                 selection=${choices[$(($answer-1))]}
@@ -634,7 +660,8 @@
         fi
     fi
 
-    if [ -z "$product" ]; then
+    if [ -z "$product" ]
+    then
         echo
         echo "Invalid lunch combo: $selection"
         return 1
@@ -644,7 +671,8 @@
     TARGET_BUILD_VARIANT=$variant \
     TARGET_PLATFORM_VERSION=$version \
     build_build_var_cache
-    if [ $? -ne 0 ]; then
+    if [ $? -ne 0 ]
+    then
         return 1
     fi
 
@@ -666,7 +694,8 @@
 
 unset COMMON_LUNCH_CHOICES_CACHE
 # Tab completion for lunch.
-function _lunch() {
+function _lunch()
+{
     local cur prev opts
     COMPREPLY=()
     cur="${COMP_WORDS[COMP_CWORD]}"
@@ -682,7 +711,8 @@
 
 # Configures the build to build unbundled apps.
 # Run tapas with one or more app names (from LOCAL_PACKAGE_NAME)
-function tapas() {
+function tapas()
+{
     local showHelp="$(echo $* | xargs -n 1 echo | \grep -E '^(help)$' | xargs)"
     local arch="$(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|arm64|x86_64)$' | xargs)"
     local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)"
@@ -709,15 +739,9 @@
 
     local product=aosp_arm
     case $arch in
-        x86)
-            product=aosp_x86
-            ;;
-        arm64)
-            product=aosp_arm64
-            ;;
-        x86_64)
-            product=aosp_x86_64
-            ;;
+      x86)    product=aosp_x86;;
+      arm64)  product=aosp_arm64;;
+      x86_64) product=aosp_x86_64;;
     esac
     if [ -z "$variant" ]; then
         variant=eng
@@ -741,13 +765,14 @@
     destroy_build_var_cache
 }
 
-function gettop {
+function gettop
+{
     local TOPFILE=build/make/core/envsetup.mk
-    if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ]; then
+    if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
         # The following circumlocution ensures we remove symlinks from TOP.
         (cd $TOP; PWD= /bin/pwd)
     else
-        if [ -f $TOPFILE ]; then
+        if [ -f $TOPFILE ] ; then
             # The following circumlocution (repeated below as well) ensures
             # that we record the true directory name and not one that is
             # faked up with symlink names.
@@ -767,7 +792,8 @@
     fi
 }
 
-function croot() {
+function croot()
+{
     local T=$(gettop)
     if [ "$T" ]; then
         if [ "$1" ]; then
@@ -780,7 +806,8 @@
     fi
 }
 
-function _croot() {
+function _croot()
+{
     local T=$(gettop)
     if [ "$T" ]; then
         local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -791,7 +818,8 @@
     fi
 }
 
-function cproj() {
+function cproj()
+{
     local TOPFILE=build/make/core/envsetup.mk
     local HERE=$PWD
     local T=
@@ -812,17 +840,17 @@
 function qpid() {
     local prepend=''
     local append=''
-    if [ "$1" == "--exact" ]; then
+    if [ "$1" = "--exact" ]; then
         prepend=' '
         append='$'
         shift
-    elif [ "$1" == "--help" -o "$1" == "-h" ]; then
+    elif [ "$1" = "--help" -o "$1" = "-h" ]; then
         echo "usage: qpid [[--exact] <process name|pid>"
         return 255
     fi
 
     local EXE="$1"
-    if [ "$EXE" ]; then
+    if [ "$EXE" ] ; then
         qpid | \grep "$prepend$EXE$append"
     else
         adb shell ps \
@@ -838,87 +866,92 @@
 #       if its core-file-size limit is not set already.
 # NOTE: Core dumps are written to ramdisk; they will not survive a reboot!
 
-function coredump_setup() {
-    echo "Getting root..."
-    adb root
-    adb wait-for-device
+function coredump_setup()
+{
+    echo "Getting root...";
+    adb root;
+    adb wait-for-device;
 
-    echo "Remounting root partition read-write..."
-    adb shell mount -w -o remount -t rootfs rootfs
-    sleep 1
-    adb wait-for-device
-    adb shell mkdir -p /cores
-    adb shell mount -t tmpfs tmpfs /cores
-    adb shell chmod 0777 /cores
+    echo "Remounting root partition read-write...";
+    adb shell mount -w -o remount -t rootfs rootfs;
+    sleep 1;
+    adb wait-for-device;
+    adb shell mkdir -p /cores;
+    adb shell mount -t tmpfs tmpfs /cores;
+    adb shell chmod 0777 /cores;
 
-    echo "Granting SELinux permission to dump in /cores..."
-    adb shell restorecon -R /cores
+    echo "Granting SELinux permission to dump in /cores...";
+    adb shell restorecon -R /cores;
 
-    echo "Set core pattern."
-    adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern'
+    echo "Set core pattern.";
+    adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';
 
     echo "Done."
 }
 
 # coredump_enable - enable core dumps for the specified process
-# $1 == PID of process (e.g., $(pid mediaserver))
+# $1 = PID of process (e.g., $(pid mediaserver))
 #
 # NOTE: coredump_setup must have been called as well for a core
 #       dump to actually be generated.
 
-function coredump_enable() {
-    local PID=$1
+function coredump_enable()
+{
+    local PID=$1;
     if [ -z "$PID" ]; then
-        printf "Expecting a PID!\n"
-        return
-    fi
-    echo "Setting core limit for $PID to infinite..."
+        printf "Expecting a PID!\n";
+        return;
+    fi;
+    echo "Setting core limit for $PID to infinite...";
     adb shell /system/bin/ulimit -p $PID -c unlimited
 }
 
 # core - send SIGV and pull the core for process
-# $1 == PID of process (e.g., $(pid mediaserver))
+# $1 = PID of process (e.g., $(pid mediaserver))
 #
 # NOTE: coredump_setup must be called once per boot for core dumps to be
 #       enabled globally.
 
-function core() {
-    local PID=$1
+function core()
+{
+    local PID=$1;
 
     if [ -z "$PID" ]; then
-        printf "Expecting a PID!\n"
-        return
-    fi
+        printf "Expecting a PID!\n";
+        return;
+    fi;
 
-    local CORENAME=core.$PID
-    local COREPATH=/cores/$CORENAME
-    local SIG=SEGV
+    local CORENAME=core.$PID;
+    local COREPATH=/cores/$CORENAME;
+    local SIG=SEGV;
 
-    coredump_enable $1
+    coredump_enable $1;
 
-    local done=0
+    local done=0;
     while [ $(adb shell "[ -d /proc/$PID ] && echo -n yes") ]; do
-        printf "\tSending SIG%s to %d...\n" $SIG $PID
-        adb shell kill -$SIG $PID
-        sleep 1
-    done
+        printf "\tSending SIG%s to %d...\n" $SIG $PID;
+        adb shell kill -$SIG $PID;
+        sleep 1;
+    done;
 
-    adb shell "while [ ! -f $COREPATH ]; do echo waiting for $COREPATH to be generated; sleep 1; done"
-    echo "Done: core is under $COREPATH on device."
+    adb shell "while [ ! -f $COREPATH ] ; do echo waiting for $COREPATH to be generated; sleep 1; done"
+    echo "Done: core is under $COREPATH on device.";
 }
 
 # systemstack - dump the current stack trace of all threads in the system process
 # to the usual ANR traces file
-function systemstack() {
+function systemstack()
+{
     stacks system_server
 }
 
 # Read the ELF header from /proc/$PID/exe to determine if the process is
 # 64-bit.
-function is64bit() {
+function is64bit()
+{
     local PID="$1"
-    if [ "$PID" ]; then
-        if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]]; then
+    if [ "$PID" ] ; then
+        if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]] ; then
             echo "64"
         else
             echo ""
@@ -930,90 +963,107 @@
 
 case `uname -s` in
     Darwin)
-        function sgrep() {
+        function sgrep()
+        {
             find -E . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.(c|h|cc|cpp|hpp|S|java|xml|sh|mk|aidl|vts|proto)' \
                 -exec grep --color -n "$@" {} +
         }
+
         ;;
     *)
-        function sgrep() {
+        function sgrep()
+        {
             find . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.\(c\|h\|cc\|cpp\|hpp\|S\|java\|xml\|sh\|mk\|aidl\|vts\|proto\)' \
                 -exec grep --color -n "$@" {} +
         }
         ;;
 esac
 
-function gettargetarch {
+function gettargetarch
+{
     get_build_var TARGET_ARCH
 }
 
-function ggrep() {
+function ggrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \
         -exec grep --color -n "$@" {} +
 }
 
-function gogrep() {
+function gogrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
         -exec grep --color -n "$@" {} +
 }
 
-function jgrep() {
+function jgrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
         -exec grep --color -n "$@" {} +
 }
 
-function cgrep() {
+function cgrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
         -exec grep --color -n "$@" {} +
 }
 
-function resgrep() {
+function resgrep()
+{
     local dir
     for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
         find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
     done
 }
 
-function mangrep() {
+function mangrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \
         -exec grep --color -n "$@" {} +
 }
 
-function owngrep() {
+function owngrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \
         -exec grep --color -n "$@" {} +
 }
 
-function sepgrep() {
+function sepgrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
         -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
 }
 
-function rcgrep() {
+function rcgrep()
+{
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \
         -exec grep --color -n "$@" {} +
 }
 
 case `uname -s` in
     Darwin)
-        function mgrep() {
+        function mgrep()
+        {
             find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
                 -exec grep --color -n "$@" {} +
         }
 
-        function treegrep() {
+        function treegrep()
+        {
             find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|hpp|S|java|xml)' \
                 -exec grep --color -n -i "$@" {} +
         }
 
         ;;
     *)
-        function mgrep() {
+        function mgrep()
+        {
             find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
                 -exec grep --color -n "$@" {} +
         }
 
-        function treegrep() {
+        function treegrep()
+        {
             find . -name .repo -prune -o -name .git -prune -o -regextype posix-egrep -iregex '.*\.(c|h|cpp|hpp|S|java|xml)' -type f \
                 -exec grep --color -n -i "$@" {} +
         }
@@ -1021,11 +1071,13 @@
         ;;
 esac
 
-function getprebuilt {
+function getprebuilt
+{
     get_abs_build_var ANDROID_PREBUILTS
 }
 
-function tracedmdump() {
+function tracedmdump()
+{
     local T=$(gettop)
     if [ ! "$T" ]; then
         echo "Couldn't locate the top of the tree.  Try setting TOP."
@@ -1036,18 +1088,18 @@
     local KERNEL=$T/prebuilts/qemu-kernel/$arch/vmlinux-qemu
 
     local TRACE=$1
-    if [ ! "$TRACE" ]; then
+    if [ ! "$TRACE" ] ; then
         echo "usage:  tracedmdump  tracename"
         return
     fi
 
-    if [ ! -r "$KERNEL" ]; then
+    if [ ! -r "$KERNEL" ] ; then
         echo "Error: cannot find kernel: '$KERNEL'"
         return
     fi
 
     local BASETRACE=$(basename $TRACE)
-    if [ "$BASETRACE" == "$TRACE" ]; then
+    if [ "$BASETRACE" = "$TRACE" ] ; then
         TRACE=$ANDROID_PRODUCT_OUT/traces/$TRACE
     fi
 
@@ -1073,23 +1125,24 @@
 
 # communicate with a running device or emulator, set up necessary state,
 # and run the hat command.
-function runhat() {
+function runhat()
+{
     # process standard adb options
     local adbTarget=""
-    if [ "$1" == "-d" -o "$1" == "-e" ]; then
+    if [ "$1" = "-d" -o "$1" = "-e" ]; then
         adbTarget=$1
         shift 1
-    elif [ "$1" == "-s" ]; then
+    elif [ "$1" = "-s" ]; then
         adbTarget="$1 $2"
         shift 2
     fi
     local adbOptions=${adbTarget}
-    #echo adbOptions == ${adbOptions}
+    #echo adbOptions = ${adbOptions}
 
     # runhat options
     local targetPid=$1
 
-    if [ "$targetPid" == "" ]; then
+    if [ "$targetPid" = "" ]; then
         echo "Usage: runhat [ -d | -e | -s serial ] target-pid"
         return
     fi
@@ -1122,7 +1175,8 @@
     hat -JXmx512m $localFile
 }
 
-function getbugreports() {
+function getbugreports()
+{
     local reports=(`adb shell ls /sdcard/bugreports | tr -d '\r'`)
 
     if [ ! "$reports" ]; then
@@ -1139,18 +1193,21 @@
     done
 }
 
-function getsdcardpath() {
+function getsdcardpath()
+{
     adb ${adbOptions} shell echo -n \$\{EXTERNAL_STORAGE\}
 }
 
-function getscreenshotpath() {
+function getscreenshotpath()
+{
     echo "$(getsdcardpath)/Pictures/Screenshots"
 }
 
-function getlastscreenshot() {
+function getlastscreenshot()
+{
     local screenshot_path=$(getscreenshotpath)
     local screenshot=`adb ${adbOptions} ls ${screenshot_path} | grep Screenshot_[0-9-]*.*\.png | sort -rk 3 | cut -d " " -f 4 | head -n 1`
-    if [ "$screenshot" == "" ]; then
+    if [ "$screenshot" = "" ]; then
         echo "No screenshots found."
         return
     fi
@@ -1158,7 +1215,8 @@
     adb ${adbOptions} pull ${screenshot_path}/${screenshot}
 }
 
-function startviewserver() {
+function startviewserver()
+{
     local port=4939
     if [ $# -gt 0 ]; then
             port=$1
@@ -1166,27 +1224,33 @@
     adb shell service call window 1 i32 $port
 }
 
-function stopviewserver() {
+function stopviewserver()
+{
     adb shell service call window 2
 }
 
-function isviewserverstarted() {
+function isviewserverstarted()
+{
     adb shell service call window 3
 }
 
-function key_home() {
+function key_home()
+{
     adb shell input keyevent 3
 }
 
-function key_back() {
+function key_back()
+{
     adb shell input keyevent 4
 }
 
-function key_menu() {
+function key_menu()
+{
     adb shell input keyevent 82
 }
 
-function smoketest() {
+function smoketest()
+{
     if [ ! "$ANDROID_PRODUCT_OUT" ]; then
         echo "Couldn't locate output files.  Try running 'lunch' first." >&2
         return
@@ -1206,7 +1270,8 @@
 }
 
 # simple shortcut to the runtest command
-function runtest() {
+function runtest()
+{
     local T=$(gettop)
     if [ ! "$T" ]; then
         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
@@ -1222,7 +1287,7 @@
     fi
     local T=$(gettop)
     local FILELIST
-    if [ ! "$OUT_DIR" == "" ]; then
+    if [ ! "$OUT_DIR" = "" ]; then
         mkdir -p $OUT_DIR
         FILELIST=$OUT_DIR/filelist
     else
@@ -1236,7 +1301,7 @@
     fi
     local lines
     lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))
-    if [[ ${#lines[@]} == 0 ]]; then
+    if [[ ${#lines[@]} = 0 ]]; then
         echo "Not found"
         return
     fi
@@ -1356,7 +1421,8 @@
 function pez {
     "$@"
     local retval=$?
-    if [ $retval -ne 0 ]; then
+    if [ $retval -ne 0 ]
+    then
         echo $'\E'"[0;31mFAILURE\e[00m"
     else
         echo $'\E'"[0;32mSUCCESS\e[00m"
@@ -1364,7 +1430,8 @@
     return $retval
 }
 
-function get_make_command() {
+function get_make_command()
+{
     # If we're in the top of an Android tree, use soong_ui.bash instead of make
     if [ -f build/soong/soong_ui.bash ]; then
         # Always use the real make if -C is passed in
@@ -1380,7 +1447,8 @@
     fi
 }
 
-function _wrap_build() {
+function _wrap_build()
+{
     if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then
       "$@"
       return $?
@@ -1404,16 +1472,16 @@
         color_reset=""
     fi
     echo
-    if [ $ret -eq 0 ]; then
+    if [ $ret -eq 0 ] ; then
         echo -n "${color_success}#### build completed successfully "
     else
         echo -n "${color_failed}#### failed to build some targets "
     fi
-    if [ $hours -gt 0 ]; then
+    if [ $hours -gt 0 ] ; then
         printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs
-    elif [ $mins -gt 0 ]; then
+    elif [ $mins -gt 0 ] ; then
         printf "(%02g:%02g (mm:ss))" $mins $secs
-    elif [ $secs -gt 0 ]; then
+    elif [ $secs -gt 0 ] ; then
         printf "(%s seconds)" $secs
     fi
     echo " ####${color_reset}"
@@ -1456,11 +1524,13 @@
     _trigger_build "modules-in-dirs" "$@"
 )
 
-function make() {
+function make()
+{
     _wrap_build $(get_make_command "$@") "$@"
 }
 
-function provision() {
+function provision()
+{
     if [ ! "$ANDROID_PRODUCT_OUT" ]; then
         echo "Couldn't locate output files.  Try running 'lunch' first." >&2
         return 1
@@ -1471,7 +1541,7 @@
     fi
 
     # Check if user really wants to do this.
-    if [ "$1" == "--no-confirmation" ]; then
+    if [ "$1" = "--no-confirmation" ]; then
         shift 1
     else
         echo "This action will reflash your device."
@@ -1480,7 +1550,7 @@
         echo ""
         echo -n "Are you sure you want to do this (yes/no)? "
         read
-        if [[ "${REPLY}" != "yes" ]]; then
+        if [[ "${REPLY}" != "yes" ]] ; then
             echo "Not taking any action. Exiting." >&2
             return 1
         fi
@@ -1505,8 +1575,7 @@
             ;;
         *zsh*)
             function check_type() { type "$1"; }
-            enable_zsh_completion
-            ;;
+            enable_zsh_completion ;;
         *)
             echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results."
             ;;
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 70ea967..490b44a 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -60,6 +60,7 @@
 import common
 import rangelib
 import sparse_img
+import verity_utils
 
 if sys.hexversion < 0x02070000:
   print("Python 2.7 or newer is required.", file=sys.stderr)
@@ -312,6 +313,56 @@
   img.Write()
   return img.name
 
+def AddCustomImages(output_zip, partition_name):
+  """Adds and signs custom images in IMAGES/.
+
+  Args:
+    output_zip: The output zip file (needs to be already open), or None to
+        write images to OPTIONS.input_tmp/.
+
+  Uses the image under IMAGES/ if it already exists. Otherwise looks for the
+  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
+
+  Raises:
+    AssertionError: If image can't be found.
+  """
+
+  partition_size = OPTIONS.info_dict.get(
+      "avb_{}_partition_size".format(partition_name))
+  key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
+  algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
+  extra_args = OPTIONS.info_dict.get(
+      "avb_{}_add_hashtree_footer_args".format(partition_name))
+  partition_size = OPTIONS.info_dict.get(
+      "avb_{}_partition_size".format(partition_name))
+
+  builder = verity_utils.CreateCustomImageBuilder(
+      OPTIONS.info_dict, partition_name, partition_size,
+      key_path, algorithm, extra_args)
+
+  for img_name in OPTIONS.info_dict.get(
+      "avb_{}_image_list".format(partition_name)).split():
+    custom_image = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
+    if os.path.exists(custom_image.name):
+      continue
+
+    custom_image_prebuilt_path = os.path.join(
+        OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
+    assert os.path.exists(custom_image_prebuilt_path), \
+      "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
+
+    shutil.copy(custom_image_prebuilt_path, custom_image.name)
+
+    if builder is not None:
+      builder.Build(custom_image.name)
+
+    custom_image.Write()
+
+  default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
+  assert os.path.exists(default), \
+      "There should be one %s.img" % (partition_name)
+  return default
+
 
 def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
   logger.info("creating %s.img...", what)
@@ -411,8 +462,9 @@
   Args:
     output_zip: The output zip file, which needs to be already open.
     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.
+        values. Only valid partition names are accepted, as partitions listed
+        in common.AVB_PARTITIONS and custom partitions listed in
+        OPTIONS.info_dict.get("avb_custom_images_partition_list")
     name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
     needed_partitions: Partitions whose descriptors should be included into the
         generated VBMeta image.
@@ -831,11 +883,20 @@
     banner("dtbo")
     partitions['dtbo'] = AddDtbo(output_zip)
 
+  # Custom images.
+  custom_partitions = OPTIONS.info_dict.get(
+      "avb_custom_images_partition_list", "").strip().split()
+  for partition_name in custom_partitions:
+    partition_name = partition_name.strip()
+    banner("custom images for " + partition_name)
+    partitions[partition_name] = AddCustomImages(output_zip, partition_name)
+
   if OPTIONS.info_dict.get("avb_enable") == "true":
     # 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[:]
+    # Currently custom_partitions are all chained to VBMeta image.
+    vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
 
     vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
     if vbmeta_system:
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 96f93a8..93e14e5 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1132,8 +1132,9 @@
   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.
+        values. Only valid partition names are accepted, as partitions listed
+        in common.AVB_PARTITIONS and custom partitions listed in
+        OPTIONS.info_dict.get("avb_custom_images_partition_list")
     name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
     needed_partitions: Partitions whose descriptors should be included into the
         generated VBMeta image.
@@ -1145,11 +1146,15 @@
   cmd = [avbtool, "make_vbmeta_image", "--output", image_path]
   AppendAVBSigningArgs(cmd, name)
 
+  custom_partitions = OPTIONS.info_dict.get(
+      "avb_custom_images_partition_list", "").strip().split()
+
   for partition, path in partitions.items():
     if partition not in needed_partitions:
       continue
     assert (partition in AVB_PARTITIONS or
-            partition in AVB_VBMETA_PARTITIONS), \
+            partition in AVB_VBMETA_PARTITIONS or
+            partition in custom_partitions), \
         'Unknown partition: {}'.format(partition)
     assert os.path.exists(path), \
         'Failed to find {} for {}'.format(path, partition)
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 52cd9a8..52b7889 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -112,6 +112,17 @@
       (e.g. "--signing_helper /path/to/helper"). The args will be appended to
       the existing ones in info dict.
 
+  --avb_extra_custom_image_key <partition=key>
+  --avb_extra_custom_image_algorithm <partition=algorithm>
+      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
+      the specified custom images mounted on the partition. Otherwise it uses
+      the existing values in info dict.
+
+  --avb_extra_custom_image_extra_args <partition=extra_args>
+      Specify any additional args that are needed to AVB-sign the custom images
+      mounted on the partition (e.g. "--signing_helper /path/to/helper"). The
+      args will be appended to the existing ones in info dict.
+
   --android_jar_path <path>
       Path to the android.jar to repack the apex file.
 """
@@ -956,12 +967,20 @@
     if extra_args:
       print('Setting extra AVB signing args for %s to "%s"' % (
           partition, extra_args))
-      args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
+      if partition in AVB_FOOTER_ARGS_BY_PARTITION:
+        args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
+      else:
+        # custom partition
+        args_key = "avb_{}_add_hashtree_footer_args".format(partition)
       misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
 
   for partition in AVB_FOOTER_ARGS_BY_PARTITION:
     ReplaceAvbPartitionSigningKey(partition)
 
+  for custom_partition in misc_info.get(
+      "avb_custom_images_partition_list", "").strip().split():
+    ReplaceAvbPartitionSigningKey(custom_partition)
+
 
 def RewriteAvbProps(misc_info):
   """Rewrites the props in AVB signing args."""
@@ -1208,6 +1227,15 @@
       OPTIONS.avb_extra_args['vbmeta_vendor'] = a
     elif o == "--avb_apex_extra_args":
       OPTIONS.avb_extra_args['apex'] = a
+    elif o == "--avb_extra_custom_image_key":
+      partition, key = a.split("=")
+      OPTIONS.avb_keys[partition] = key
+    elif o == "--avb_extra_custom_image_algorithm":
+      partition, algorithm = a.split("=")
+      OPTIONS.avb_algorithms[partition] = algorithm
+    elif o == "--avb_extra_custom_image_extra_args":
+      partition, extra_args = a.split("=")
+      OPTIONS.avb_extra_args[partition] = extra_args
     else:
       return False
     return True
@@ -1252,6 +1280,9 @@
           "avb_vbmeta_vendor_algorithm=",
           "avb_vbmeta_vendor_key=",
           "avb_vbmeta_vendor_extra_args=",
+          "avb_extra_custom_image_key=",
+          "avb_extra_custom_image_algorithm=",
+          "avb_extra_custom_image_extra_args=",
       ],
       extra_option_handler=option_handler)
 
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 1b918cc..69be511 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -352,8 +352,13 @@
     cmd = [info_dict['avb_avbtool'], 'verify_image', '--image', image,
            '--follow_chain_partitions']
 
+    # Custom images.
+    custom_partitions = info_dict.get(
+        "avb_custom_images_partition_list", "").strip().split()
+
     # Append the args for chained partitions if any.
-    for partition in common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS:
+    for partition in (common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS +
+                      tuple(custom_partitions)):
       key_name = 'avb_' + partition + '_key_path'
       if info_dict.get(key_name) is not None:
         if info_dict.get('ab_update') != 'true' and partition == 'recovery':
diff --git a/tools/releasetools/verity_utils.py b/tools/releasetools/verity_utils.py
index e7f84f5..fc83689 100644
--- a/tools/releasetools/verity_utils.py
+++ b/tools/releasetools/verity_utils.py
@@ -695,3 +695,22 @@
       raise HashtreeInfoGenerationError("Failed to reconstruct the verity tree")
 
     return self.hashtree_info
+
+
+def CreateCustomImageBuilder(info_dict, partition_name, partition_size,
+                            key_path, algorithm, signing_args):
+  builder = None
+  if info_dict.get("avb_enable") == "true":
+    builder = VerifiedBootVersion2VerityImageBuilder(
+        partition_name,
+        partition_size,
+        VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER,
+        info_dict.get("avb_avbtool"),
+        key_path,
+        algorithm,
+        # Salt is None because custom images have no fingerprint property to be
+        # used as the salt.
+        None,
+        signing_args)
+
+  return builder