Merge changes from topic "manifest_fixer_targetSdkVersion"

* changes:
  Pass --library to manifest_fixer.py for android libraries
  Fix dependencies for obfuscated instrumentation tests
  Move manifest fixing to a separate rule
diff --git a/core/Makefile b/core/Makefile
index d694f85..9c26b8b 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1211,6 +1211,7 @@
     $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "product_squashfs_block_size=$(BOARD_PRODUCTIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
     $(if $(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "product_squashfs_disable_4k_align=$(BOARD_PRODUCTIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
     $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PRODUCT_BASE_FS_PATH),$(hide) echo "product_base_fs_file=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PRODUCT_BASE_FS_PATH)" >> $(1))
+    $(if $(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "product_reserved_size=$(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
 )
 $(if $(filter $(2),productservices),\
     $(if $(BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "productservices_fs_type=$(BOARD_PRODUCT_SERVICESIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
@@ -1222,6 +1223,7 @@
     $(if $(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "productservices_squashfs_compressor_opt=$(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
     $(if $(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_BLOCK_SIZE),$(hide) echo "productservices_squashfs_block_size=$(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_BLOCK_SIZE)" >> $(1))
     $(if $(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_DISABLE_4K_ALIGN),$(hide) echo "productservices_squashfs_disable_4k_align=$(BOARD_PRODUCT_SERVICESIMAGE_SQUASHFS_DISABLE_4K_ALIGN)" >> $(1))
+    $(if $(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_RESERVED_SIZE),$(hide) echo "productservices_reserved_size=$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_RESERVED_SIZE)" >> $(1))
 )
 $(if $(filter $(2),oem),\
     $(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
@@ -1283,7 +1285,7 @@
     $(hide) echo "recovery_as_boot=true" >> $(1))
 $(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),\
     $(hide) echo "system_root_image=true" >> $(1)
-    $(hide) echo "ramdisk_dir=$(TARGET_ROOT_OUT)" >> $(1))
+    $(hide) echo "root_dir=$(TARGET_ROOT_OUT)" >> $(1))
 $(if $(USE_LOGICAL_PARTITIONS),$(hide) echo "use_logical_partitions=true" >> $(1))
 $(if $(3),$(hide) $(foreach kv,$(3),echo "$(kv)" >> $(1);))
 endef
@@ -2264,8 +2266,11 @@
   $(call generate-image-prop-dictionary, $(productimage_intermediates)/product_image_info.txt,product,skip_fsck=true)
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
-      $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT)
-  $(hide) $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),$(BOARD_PRODUCTIMAGE_PARTITION_SIZE))
+      $(TARGET_OUT_PRODUCT) $(productimage_intermediates)/product_image_info.txt $(INSTALLED_PRODUCTIMAGE_TARGET) $(TARGET_OUT) \
+      $(productimage_intermediates)/generated_product_image_info.txt
+  $(hide) $(call assert-max-image-size,$(INSTALLED_PRODUCTIMAGE_TARGET),\
+      $(call read-image-prop-dictionary,\
+          $(productimage_intermediates)/generated_product_image_info.txt,product_size))
 endef
 
 # We just build this directly to the install location.
@@ -2316,8 +2321,11 @@
   $(call generate-userimage-prop-dictionary, $(productservicesimage_intermediates)/productservices_image_info.txt, skip_fsck=true)
   $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
       ./build/tools/releasetools/build_image.py \
-      $(TARGET_OUT_PRODUCT_SERVICES) $(productservicesimage_intermediates)/productservices_image_info.txt $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET) $(TARGET_OUT)
-  $(hide) $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),$(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE))
+      $(TARGET_OUT_PRODUCT_SERVICES) $(productservicesimage_intermediates)/productservices_image_info.txt $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET) $(TARGET_OUT) \
+      $(productservicesimage_intermediates)/generated_productservices_image_info.txt
+  $(hide) $(call assert-max-image-size,$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET),\
+      $(call read-image-prop-dictionary,\
+          $(productservicesimage_intermediates)/generated_productservices_image_info.txt,productservices_size))
 endef
 
 # We just build this directly to the install location.
@@ -2549,6 +2557,52 @@
 endif # BOARD_AVB_ENABLE
 
 # -----------------------------------------------------------------
+# Check image sizes <= size of super partition
+
+ifeq (,$(TARGET_BUILD_APPS))
+# Do not check for apps-only build
+
+ifeq (true,$(USE_LOGICAL_PARTITIONS))
+ifdef BOARD_SUPER_PARTITION_SIZE
+ifdef BOARD_SUPER_PARTITION_PARTITION_LIST
+
+droid_targets: check_android_partition_sizes
+
+.PHONY: check_android_partition_sizes
+
+# Add image dependencies so that generated_*_image_info.txt are written before checking.
+ifneq (,$(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
+check_android_partition_sizes: $(BUILT_SYSTEMIMAGE)
+endif
+ifneq (,$(filter vendor,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
+check_android_partition_sizes: $(INSTALLED_VENDORIMAGE_TARGET)
+endif
+ifneq (,$(filter product,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
+check_android_partition_sizes: $(INSTALLED_PRODUCTIMAGE_TARGET)
+endif
+ifneq (,$(filter productservices,$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
+check_android_partition_sizes: $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET)
+endif
+
+check_android_partition_sizes:
+	partition_size_list="$(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST),$(call read-image-prop-dictionary,$($(p)image_intermediates)/generated_$(p)_image_info.txt,$(p)_size))"; \
+	sum_sizes_expr=$$(sed -e 's/ /+/g' <<< "$${partition_size_list}"); \
+	if [ $$(( $${sum_sizes_expr} )) -gt $(BOARD_SUPER_PARTITION_SIZE) ]; then \
+		echo 'The sum of sizes of all logical partitions is larger than BOARD_SUPER_PARTITION_SIZE.'; \
+		echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '>' $(BOARD_SUPER_PARTITION_SIZE); \
+		exit 1; \
+	else \
+		echo 'The sum of sizes of all logical partitions is within BOARD_SUPER_PARTITION_SIZE:' \
+		    $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '<=' $(BOARD_SUPER_PARTITION_SIZE); \
+	fi
+
+endif # BOARD_SUPER_PARTITION_PARTITION_LIST
+endif # BOARD_SUPER_PARTITION_SIZE
+endif # USE_LOGICAL_PARTITIONS
+
+endif # TARGET_BUILD_APPS
+
+# -----------------------------------------------------------------
 # bring in the installer image generation defines if necessary
 ifeq ($(TARGET_USE_DISKINSTALLER),true)
 include bootable/diskinstaller/config.mk
@@ -3193,6 +3247,27 @@
 updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
 
 # -----------------------------------------------------------------
+# A zip of the appcompat directory containing logs
+APPCOMPAT_ZIP := $(PRODUCT_OUT)/appcompat.zip
+# For apps_only build we'll establish the dependency later in build/make/core/main.mk.
+ifndef TARGET_BUILD_APPS
+$(APPCOMPAT_ZIP): $(INSTALLED_SYSTEMIMAGE) \
+		$(INSTALLED_BOOTIMAGE_TARGET) \
+		$(INSTALLED_USERDATAIMAGE_TARGET) \
+		$(INSTALLED_VENDORIMAGE_TARGET) \
+		$(INSTALLED_PRODUCTIMAGE_TARGET) \
+		$(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET)
+endif
+$(APPCOMPAT_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,appcompat)/filelist
+$(APPCOMPAT_ZIP): $(SOONG_ZIP)
+	@echo "appcompat logs: $@"
+	$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)
+	$(hide) mkdir -p $(dir $@) $(PRODUCT_OUT)/appcompat $(dir $(PRIVATE_LIST_FILE))
+	$(hide) find $(PRODUCT_OUT)/appcompat | sort >$(PRIVATE_LIST_FILE)
+	$(hide) $(SOONG_ZIP) -d -o $@ -C $(PRODUCT_OUT)/appcompat -l $(PRIVATE_LIST_FILE)
+
+
+# -----------------------------------------------------------------
 # A zip of the symbols directory.  Keep the full paths to make it
 # more obvious where these files came from.
 #
@@ -3416,6 +3491,7 @@
 	$(ALL_DEFAULT_INSTALLED_MODULES) \
 	$(INSTALLED_RAMDISK_TARGET) \
 	$(ALL_DOCS) \
+	$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/metalava-api-stubs-docs_annotations.zip \
 	$(ALL_SDK_FILES)
 endif
 
@@ -3448,6 +3524,7 @@
 	$(OUT_DOCS)/offline-sdk-timestamp \
 	$(SYMBOLS_ZIP) \
 	$(COVERAGE_ZIP) \
+	$(APPCOMPAT_ZIP) \
 	$(INSTALLED_SYSTEMIMAGE) \
 	$(INSTALLED_QEMU_SYSTEMIMAGE) \
 	$(INSTALLED_QEMU_VENDORIMAGE) \
diff --git a/core/base_rules.mk b/core/base_rules.mk
index b4cb55b..744740f 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -356,6 +356,7 @@
 ## make clean- targets
 ###########################################################
 cleantarget := clean-$(my_register_name)
+.PHONY: $(cleantarget)
 $(cleantarget) : PRIVATE_MODULE := $(my_register_name)
 $(cleantarget) : PRIVATE_CLEAN_FILES := \
     $(LOCAL_BUILT_MODULE) \
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 75587bb..e7116a5 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -253,6 +253,7 @@
 LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=
 LOCAL_DROIDDOC_STUBS_SRCJAR :=
 LOCAL_DROIDDOC_DOC_ZIP :=
+LOCAL_DROIDDOC_ANNOTATIONS_ZIP :=
 # '',true
 LOCAL_SOURCE_FILES_ALL_GENERATED:=
 LOCAL_SRC_FILES:=
diff --git a/core/config.mk b/core/config.mk
index f7a8176..d49d7d5 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -719,6 +719,7 @@
 DEXDUMP := $(HOST_OUT_EXECUTABLES)/dexdump2$(BUILD_EXECUTABLE_SUFFIX)
 PROFMAN := $(HOST_OUT_EXECUTABLES)/profman
 HIDDENAPI := $(HOST_OUT_EXECUTABLES)/hiddenapi
+CLASS2GREYLIST := $(HOST_OUT_EXECUTABLES)/class2greylist
 
 FINDBUGS_DIR := external/owasp/sanitizer/tools/findbugs/bin
 FINDBUGS := $(FINDBUGS_DIR)/findbugs
@@ -931,6 +932,30 @@
 endif
 endif
 
+ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_SIZE),)
+ifneq ($(BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE),)
+$(error Should not define BOARD_PRODUCTIMAGE_PARTITION_SIZE and \
+    BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE together)
+endif
+endif
+
+ifneq ($(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE),)
+ifneq ($(BOARD_PRODUCT_SERVICESIMAGE_PARTITION_RESERVED_SIZE),)
+$(error Should not define BOARD_PRODUCT_SERVICESIMAGE_PARTITION_SIZE and \
+    BOARD_PRODUCT_SERVICESIMAGE_PARTITION_RESERVED_SIZE together)
+endif
+endif
+
+ifdef BOARD_SUPER_PARTITION_PARTITION_LIST
+# BOARD_SUPER_PARTITION_PARTITION_LIST: a list of the following tokens
+valid_super_partition_list := system vendor product productservices
+ifneq (,$(filter-out $(valid_super_partition_list),$(BOARD_SUPER_PARTITION_PARTITION_LIST)))
+$(error BOARD_SUPER_PARTITION_PARTITION_LIST contains invalid partition name. \
+        Valid names are $(valid_super_partition_list).)
+endif
+valid_super_partition_list :=
+endif # BOARD_SUPER_PARTITION_PARTITION_LIST
+
 endif # USE_LOGICAL_PARTITIONS
 
 # ###############################################################
diff --git a/core/definitions.mk b/core/definitions.mk
index 87ec31b..1550206 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2854,6 +2854,16 @@
     PRIVATE_DEX_INPUTS := $$(PRIVATE_DEX_INPUTS) $(1)
 endef
 
+# Generate a greylist.txt from a classes.jar
+define hiddenapi-generate-greylist-txt
+$(2): $(1) $(CLASS2GREYLIST)
+	$(CLASS2GREYLIST) $(1) > $(2)
+
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(2)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): \
+    PRIVATE_GREYLIST_INPUTS := $$(PRIVATE_GREYLIST_INPUTS) $(2)
+endef
+
 # File names for intermediate dex files of `hiddenapi-copy-soong-jar`.
 hiddenapi-soong-input-dex = $(dir $(1))/hiddenapi/dex-input/classes.dex
 hiddenapi-soong-output-dex = $(dir $(1))/hiddenapi/dex-output/classes.dex
diff --git a/core/dex_preopt_libart_boot.mk b/core/dex_preopt_libart_boot.mk
index 977f852..5a68738 100644
--- a/core/dex_preopt_libart_boot.mk
+++ b/core/dex_preopt_libart_boot.mk
@@ -100,7 +100,9 @@
 		$(PRIVATE_BOOT_IMAGE_FLAGS) \
 		$(addprefix --dex-file=,$(LIBART_TARGET_BOOT_DEX_FILES)) \
 		$(addprefix --dex-location=,$(LIBART_TARGET_BOOT_DEX_LOCATIONS)) \
+		--generate-debug-info --generate-build-id \
 		--oat-symbols=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)LIBART_TARGET_BOOT_OAT_UNSTRIPPED) \
+		--strip \
 		--oat-file=$(patsubst %.art,%.oat,$@) \
 		--oat-location=$(patsubst %.art,%.oat,$($(PRIVATE_2ND_ARCH_VAR_PREFIX)LIBART_BOOT_IMAGE_FILENAME)) \
 		--image=$@ --base=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
@@ -109,7 +111,6 @@
 		--instruction-set-features=$($(PRIVATE_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
 		--android-root=$(PRODUCT_OUT)/system \
 		--runtime-arg -Xnorelocate --compile-pic \
-		--no-generate-debug-info --generate-build-id \
 		--multi-image --no-inline-from=core-oj.jar \
 		--abort-on-hard-verifier-error \
 		--abort-on-soft-verifier-error \
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index deaee56..5425e31 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -287,6 +287,7 @@
 	$(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_DOCS_DIR) && zip -rqX $$F * )
 
 $(LOCAL_MODULE)-docs.zip : $(out_zip)
+.PHONY: $(LOCAL_MODULE)-docs.zip
 
 $(call dist-for-goals,docs,$(out_zip))
 
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index db24542..2aed61e 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -93,6 +93,7 @@
 
 javac-check : $(full_classes_compiled_jar)
 javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar)
+.PHONY: javac-check-$(LOCAL_MODULE)
 
 $(full_classes_combined_jar): $(full_classes_compiled_jar) \
                               $(jar_manifest_file) \
diff --git a/core/java.mk b/core/java.mk
index 5f7a43a..0d898e7 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -79,6 +79,7 @@
 built_dex_hiddenapi := $(intermediates.COMMON)/dex-hiddenapi/classes.dex
 full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
 java_source_list_file := $(intermediates.COMMON)/java-source-list
+greylist_txt := $(intermediates.COMMON)/greylist.txt
 
 
 ifeq ($(LOCAL_MODULE_CLASS)$(LOCAL_SRC_FILES)$(LOCAL_STATIC_JAVA_LIBRARIES)$(LOCAL_SOURCE_FILES_ALL_GENERATED),APPS)
@@ -308,6 +309,7 @@
 
 javac-check : $(full_classes_compiled_jar)
 javac-check-$(LOCAL_MODULE) : $(full_classes_compiled_jar)
+.PHONY: javac-check-$(LOCAL_MODULE)
 
 $(full_classes_combined_jar): PRIVATE_DONT_DELETE_JAR_META_INF := $(LOCAL_DONT_DELETE_JAR_META_INF)
 $(full_classes_combined_jar): $(full_classes_compiled_jar) \
@@ -545,6 +547,13 @@
 endif
 
 ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),) # is_boot_jar
+  # Derive API greylist from the classes jar.
+  # We use full_classes_proguard_jar here, as that is what is converted to dex
+  # later on. The difference is academic currently, as we don't proguard any
+  # bootclasspath code at the moment. If we were to do that, we should add keep
+  # rules for all members with the @UnsupportedAppUsage annotation.
+  $(eval $(call hiddenapi-generate-greylist-txt,$(full_classes_proguard_jar),$(greylist_txt)))
+  LOCAL_INTERMEDIATE_TARGETS += $(greylist_txt)
   $(eval $(call hiddenapi-copy-dex-files,$(built_dex_intermediate),$(built_dex_hiddenapi)))
   built_dex_copy_from := $(built_dex_hiddenapi)
 else # !is_boot_jar
@@ -577,6 +586,7 @@
 findbugs_html := $(PRODUCT_OUT)/findbugs/$(LOCAL_MODULE).html
 $(findbugs_html) : PRIVATE_XML_FILE := $(findbugs_xml)
 $(LOCAL_MODULE)-findbugs : $(findbugs_html)
+.PHONY: $(LOCAL_MODULE)-findbugs
 $(findbugs_html) : $(findbugs_xml)
 	@mkdir -p $(dir $@)
 	@echo ConvertXmlToText: $@
diff --git a/core/main.mk b/core/main.mk
index 7d27ef3..a991d88 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -977,6 +977,39 @@
 
 ifdef FULL_BUILD
   product_FILES := $(call product-installed-files, $(INTERNAL_PRODUCT))
+
+  # Verify the artifact path requirements made by included products.
+  all_offending_files :=
+  $(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\
+    $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \
+    $(eval ### Verify that the product only produces files inside its path requirements.) \
+    $(eval whitelist := $(PRODUCTS.$(makefile).ARTIFACT_PATH_WHITELIST)) \
+    $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \
+    $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
+    $(eval files := $(call product-installed-files, $(makefile))) \
+    $(eval files := $(filter-out $(TARGET_OUT_FAKE)/% $(HOST_OUT)/%,$(files))) \
+    $(eval # RROs become REQUIRED by the source module, but are always placed on the vendor partition.) \
+    $(eval files := $(filter-out %__auto_generated_rro.apk,$(files))) \
+    $(eval offending_files := $(filter-out $(path_patterns) $(whitelist_patterns),$(files))) \
+    $(call maybe-print-list-and-error,$(offending_files),$(makefile) produces files outside its artifact path requirement.) \
+    $(eval unused_whitelist := $(filter-out $(files),$(whitelist_patterns))) \
+    $(call maybe-print-list-and-error,$(unused_whitelist),$(makefile) includes redundant whitelist entries in its artifact path requirement.) \
+    $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \
+    $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_FILES))) \
+    $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \
+    $(eval all_offending_files += $(files_in_requirement)) \
+    $(eval whitelist := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST)) \
+    $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
+    $(eval offending_files := $(filter-out $(whitelist_patterns),$(files_in_requirement))) \
+    $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS),\
+      $(call maybe-print-list-and-error,$(offending_files),$(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement.) \
+      $(eval unused_whitelist := $(filter-out $(extra_files),$(whitelist_patterns))) \
+      $(call maybe-print-list-and-error,$(unused_whitelist),$(INTERNAL_PRODUCT) includes redundant artifact path requirement whitelist entries.) \
+    ) \
+  )
+$(PRODUCT_OUT)/offending_artifacts.txt:
+	rm -f $@
+	$(foreach f,$(sort $(all_offending_files)),echo $(f) >> $@;)
 else
   # We're not doing a full build, and are probably only including
   # a subset of the module makefiles.  Don't try to build any modules
@@ -985,34 +1018,6 @@
   product_FILES :=
 endif
 
-# Verify the artifact path requirements made by included products.
-$(foreach makefile,$(ARTIFACT_PATH_REQUIREMENT_PRODUCTS),\
-  $(eval requirements := $(PRODUCTS.$(makefile).ARTIFACT_PATH_REQUIREMENTS)) \
-  $(eval ### Verify that the product only produces files inside its path requirements.) \
-  $(eval whitelist := $(PRODUCTS.$(makefile).ARTIFACT_PATH_WHITELIST)) \
-  $(eval path_patterns := $(call resolve-product-relative-paths,$(requirements),%)) \
-  $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
-  $(eval files := $(call product-installed-files, $(makefile))) \
-  $(eval files := $(filter-out $(TARGET_OUT_FAKE)/% $(HOST_OUT)/%,$(files))) \
-  $(eval # RROs become REQUIRED by the source module, but are always placed on the vendor partition.) \
-  $(eval files := $(filter-out %__auto_generated_rro.apk,$(files))) \
-  $(eval offending_files := $(filter-out $(path_patterns) $(whitelist_patterns),$(files))) \
-  $(call maybe-print-list-and-error,$(offending_files),$(makefile) produces files outside its artifact path requirement.) \
-  $(eval unused_whitelist := $(filter-out $(files),$(whitelist_patterns))) \
-  $(call maybe-print-list-and-error,$(unused_whitelist),$(makefile) includes redundant whitelist entries in its artifact path requirement.) \
-  $(eval ### Optionally verify that nothing else produces files inside this artifact path requirement.) \
-  $(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS),\
-    $(eval extra_files := $(filter-out $(files) $(HOST_OUT)/%,$(product_FILES))) \
-    $(eval whitelist := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST)) \
-    $(eval whitelist_patterns := $(call resolve-product-relative-paths,$(whitelist))) \
-    $(eval files_in_requirement := $(filter $(path_patterns),$(extra_files))) \
-    $(eval offending_files := $(filter-out $(whitelist_patterns),$(files_in_requirement))) \
-    $(call maybe-print-list-and-error,$(offending_files),$(INTERNAL_PRODUCT) produces files inside $(makefile)s artifact path requirement.) \
-    $(eval unused_whitelist := $(filter-out $(extra_files),$(whitelist_patterns))) \
-    $(call maybe-print-list-and-error,$(unused_whitelist),$(INTERNAL_PRODUCT) includes redundant artifact path requirement whitelist entries.) \
-  ) \
-)
-
 ifeq (0,1)
   $(info product_FILES for $(TARGET_DEVICE) ($(INTERNAL_PRODUCT)):)
   $(foreach p,$(product_FILES),$(info :   $(p)))
@@ -1258,6 +1263,9 @@
   $(COVERAGE_ZIP) : $(apps_only_installed_files)
   $(call dist-for-goals,apps_only, $(COVERAGE_ZIP))
 
+  $(APPCOMPAT_ZIP) : $(apps_only_installed_files)
+  $(call dist-for-goals,apps_only, $(APPCOMPAT_ZIP))
+
 .PHONY: apps_only
 apps_only: $(unbundled_build_modules)
 
@@ -1279,6 +1287,7 @@
     $(BUILT_OTATOOLS_PACKAGE) \
     $(SYMBOLS_ZIP) \
     $(COVERAGE_ZIP) \
+    $(APPCOMPAT_ZIP) \
     $(INSTALLED_FILES_FILE) \
     $(INSTALLED_FILES_JSON) \
     $(INSTALLED_FILES_FILE_VENDOR) \
@@ -1334,26 +1343,6 @@
 # Building a full system-- the default is to build droidcore
 droid_targets: droidcore dist_files
 
-ifdef USE_LOGICAL_PARTITIONS
-ifdef BOARD_SUPER_PARTITION_SIZE
-ifdef BOARD_SUPER_PARTITION_PARTITION_LIST
-
-droid_targets: check_android_partition_sizes
-
-.PHONY: check_android_partition_sizes
-check_android_partition_sizes: partition_size_list=$(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST),$(BOARD_$(call to-upper,$(p))IMAGE_PARTITION_SIZE))
-check_android_partition_sizes: sum_sizes_expr=$(subst $(space),+,$(partition_size_list))
-check_android_partition_sizes:
-	if [ $$(( $(sum_sizes_expr) )) -gt $(BOARD_SUPER_PARTITION_SIZE) ]; then \
-		echo The sum of sizes of all logical partitions is larger than BOARD_SUPER_PARTITION_SIZE.; \
-		echo $(sum_sizes_expr) == $$(( $(sum_sizes_expr) )) '>' $(BOARD_SUPER_PARTITION_SIZE); \
-		exit 1; \
-	fi
-
-endif # BOARD_SUPER_PARTITION_PARTITION_LIST
-endif # BOARD_SUPER_PARTITION_SIZE
-endif # USE_LOGICAL_PARTITIONS
-
 endif # TARGET_BUILD_APPS
 
 .PHONY: docs
@@ -1366,13 +1355,15 @@
     $(ALL_SDK_TARGETS) \
     $(SYMBOLS_ZIP) \
     $(COVERAGE_ZIP) \
+    $(APPCOMPAT_ZIP) \
     $(INSTALLED_BUILD_PROP_TARGET) \
 )
 
 # umbrella targets to assit engineers in verifying builds
 .PHONY: java native target host java-host java-target native-host native-target \
         java-host-tests java-target-tests native-host-tests native-target-tests \
-        java-tests native-tests host-tests target-tests tests java-dex
+        java-tests native-tests host-tests target-tests tests java-dex \
+        native-host-cross
 # some synonyms
 .PHONY: host-java target-java host-native target-native \
         target-java-tests target-native-tests
diff --git a/core/native_test_config_template.xml b/core/native_test_config_template.xml
index a960529..a88d57c 100644
--- a/core/native_test_config_template.xml
+++ b/core/native_test_config_template.xml
@@ -15,6 +15,8 @@
 -->
 <!-- This test config file is auto-generated. -->
 <configuration description="Runs {MODULE}.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
diff --git a/core/product-graph.mk b/core/product-graph.mk
index 51985df..696aabf 100644
--- a/core/product-graph.mk
+++ b/core/product-graph.mk
@@ -145,3 +145,4 @@
 	dot -Tsvg -Nshape=box -o $@ $<
 
 product-graph: $(products_pdf) $(products_svg)
+.PHONY: product-graph
diff --git a/core/product.mk b/core/product.mk
index 55e1d04..ff2e7e7 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -395,6 +395,8 @@
 _product_stash_var_list += \
 	BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE \
 	BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE \
+	BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE \
+	BOARD_PRODUCT_SERVICESIMAGE_PARTITION_RESERVED_SIZE \
 	BOARD_SUPER_PARTITION_SIZE \
 	BOARD_SUPER_PARTITION_PARTITION_LIST \
 
diff --git a/core/soong_java_prebuilt.mk b/core/soong_java_prebuilt.mk
index ef71107..44b4f5c 100644
--- a/core/soong_java_prebuilt.mk
+++ b/core/soong_java_prebuilt.mk
@@ -19,6 +19,7 @@
 full_classes_pre_proguard_jar := $(intermediates.COMMON)/classes-pre-proguard.jar
 full_classes_header_jar := $(intermediates.COMMON)/classes-header.jar
 common_javalib.jar := $(intermediates.COMMON)/javalib.jar
+greylist_txt := $(intermediates.COMMON)/greylist.txt
 
 $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(full_classes_jar)))
 $(eval $(call copy-one-file,$(LOCAL_PREBUILT_MODULE_FILE),$(full_classes_pre_proguard_jar)))
@@ -33,6 +34,10 @@
 $(call dist-for-goals,docs,$(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip)
 endif
 
+ifdef LOCAL_DROIDDOC_ANNOTATIONS_ZIP
+$(eval $(call copy-one-file,$(LOCAL_DROIDDOC_ANNOTATIONS_ZIP),$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(LOCAL_MODULE)_annotations.zip))
+endif
+
 ifdef LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR
   $(eval $(call copy-one-file,$(LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR),\
     $(intermediates.COMMON)/jacoco-report-classes.jar))
@@ -82,6 +87,11 @@
   ifneq ($(LOCAL_UNINSTALLABLE_MODULE),true)
     ifndef LOCAL_IS_HOST_MODULE
       ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),)  # is_boot_jar
+        # Derive greylist from classes.jar.
+        # We use full_classes_jar here, which is the post-proguard jar (on the basis that we also
+        # have a full_classes_pre_proguard_jar). This is consistent with the equivalent code in
+        # java.mk.
+        $(eval $(call hiddenapi-generate-greylist-txt,$(full_classes_jar),$(greylist_txt)))
         $(eval $(call hiddenapi-copy-soong-jar,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
       else # !is_boot_jar
         $(eval $(call copy-one-file,$(LOCAL_SOONG_DEX_JAR),$(common_javalib.jar)))
@@ -131,6 +141,7 @@
 
 javac-check : $(full_classes_jar)
 javac-check-$(LOCAL_MODULE) : $(full_classes_jar)
+.PHONY: javac-check-$(LOCAL_MODULE)
 
 ifndef LOCAL_IS_HOST_MODULE
 ifeq ($(LOCAL_SDK_VERSION),system_current)
diff --git a/core/tasks/tools/compatibility.mk b/core/tasks/tools/compatibility.mk
index 6117414..34ac1d0 100644
--- a/core/tasks/tools/compatibility.mk
+++ b/core/tasks/tools/compatibility.mk
@@ -48,6 +48,7 @@
 $(compatibility_zip): $(test_artifacts) $(test_tools) $(test_suite_prebuilt_tools) $(test_suite_dynamic_config) $(SOONG_ZIP) | $(ADB) $(ACP)
 # Make dir structure
 	$(hide) mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases
+	$(hide) echo $(BUILD_NUMBER_FROM_FILE) > $(PRIVATE_OUT_DIR)/tools/version.txt
 # Copy tools
 	$(hide) $(ACP) -fp $(PRIVATE_TOOLS) $(PRIVATE_OUT_DIR)/tools
 	$(if $(PRIVATE_DYNAMIC_CONFIG),$(hide) $(ACP) -fp $(PRIVATE_DYNAMIC_CONFIG) $(PRIVATE_OUT_DIR)/testcases/$(PRIVATE_SUITE_NAME).dynamic)
diff --git a/core/tasks/vndk.mk b/core/tasks/vndk.mk
index ba48df7..1bde7a6 100644
--- a/core/tasks/vndk.mk
+++ b/core/tasks/vndk.mk
@@ -145,8 +145,10 @@
 # vndk_snapshot_zip
 vndk_snapshot_variant := $(vndk_snapshot_out)/$(TARGET_ARCH)
 binder :=
-ifneq ($(TARGET_USES_64_BIT_BINDER), true)
-  binder := binder32
+ifneq ($(TARGET_IS_64_BIT), true)
+  ifneq ($(TARGET_USES_64_BIT_BINDER), true)
+    binder := binder32
+  endif
 endif
 vndk_lib_dir := $(subst $(space),/,$(strip $(vndk_snapshot_variant) $(binder) arch-$(TARGET_ARCH)-$(TARGET_ARCH_VARIANT)))
 vndk_lib_dir_2nd := $(subst $(space),/,$(strip $(vndk_snapshot_variant) $(binder) arch-$(TARGET_2ND_ARCH)-$(TARGET_2ND_ARCH_VARIANT)))
diff --git a/target/board/BoardConfigEmuCommon.mk b/target/board/BoardConfigEmuCommon.mk
index 1d58eab..e8a562a 100644
--- a/target/board/BoardConfigEmuCommon.mk
+++ b/target/board/BoardConfigEmuCommon.mk
@@ -11,9 +11,6 @@
 BOARD_USES_GENERIC_AUDIO := true
 TARGET_BOOTLOADER_BOARD_NAME := goldfish_$(TARGET_ARCH)
 
-TARGET_USES_64_BIT_BINDER := true
-TARGET_USES_MKE2FS := true
-
 # no hardware camera
 USE_CAMERA_STUB := true
 
@@ -29,22 +26,14 @@
 USE_OPENGL_RENDERER := true
 
 TARGET_COPY_OUT_VENDOR := vendor
+
 # ~100 MB vendor image. Please adjust system image / vendor image sizes
 # when finalizing them.
 BOARD_VENDORIMAGE_PARTITION_SIZE := 100000000
 BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
 BOARD_FLASH_BLOCK_SIZE := 512
-TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
 DEVICE_MATRIX_FILE   := device/generic/goldfish/compatibility_matrix.xml
 
-# Android generic system image always create metadata partition
-BOARD_USES_METADATA_PARTITION := true
-
-# Set this to create /cache mount point for non-A/B devices that mounts /cache.
-# The partition size doesn't matter, just to make build pass.
-BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
-BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216
-
 BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/common
 BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true
 
diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk
index 237cd28..24614de 100644
--- a/target/board/BoardConfigGsiCommon.mk
+++ b/target/board/BoardConfigGsiCommon.mk
@@ -3,6 +3,13 @@
 # Common compile-time definitions for GSI
 #
 
+# system.img is always ext4 with sparse option
+TARGET_USERIMAGES_USE_EXT4 := true
+# TODO(b/63790380): emulator doesn't support sparse yet
+#TARGET_USERIMAGES_SPARSE_EXT_DISABLED := false
+TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
+TARGET_USES_MKE2FS := true
+
 # Android Verified Boot (AVB):
 #   Builds a special vbmeta.img that disables AVB verification.
 #   Otherwise, AVB will prevent the device from booting the generic system.img.
@@ -20,9 +27,20 @@
 endif
 BOARD_VNDK_VERSION := current
 
-# Pi GSI supports system-as-root
+# system-as-root is mandatory from Android P
 TARGET_NO_RECOVERY := true
 BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
 
+# 64 bits binder interface is mandatory from Android P
+TARGET_USES_64_BIT_BINDER := true
+
+# Android generic system image always create metadata partition
+BOARD_USES_METADATA_PARTITION := true
+
+# Set this to create /cache mount point for non-A/B devices that mounts /cache.
+# The partition size doesn't matter, just to make build pass.
+BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
+BOARD_CACHEIMAGE_PARTITION_SIZE := 16777216
+
 # Audio: must using XML format for Treblized devices
 USE_XML_AUDIO_POLICY_CONF := 1
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index 38d294b..8113ae3 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -48,7 +48,6 @@
 include build/make/target/board/BoardConfigEmuCommon.mk
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_USERIMAGES_USE_EXT4 := true
 # Partition size is default 1.5GB (1536MB) for 64 bits projects
 BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1610612736
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index caf0cee..ad6d229 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -55,7 +55,6 @@
 include build/make/target/board/BoardConfigEmuCommon.mk
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_USERIMAGES_USE_EXT4 := true
 # Partition size is default 1.5GB (1536MB) for 64 bits projects
 BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1610612736
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
diff --git a/target/board/generic_x86/BoardConfig.mk b/target/board/generic_x86/BoardConfig.mk
index 9a45188..c8ba2cf 100644
--- a/target/board/generic_x86/BoardConfig.mk
+++ b/target/board/generic_x86/BoardConfig.mk
@@ -23,6 +23,5 @@
 include build/make/target/board/BoardConfigEmuCommon.mk
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_USERIMAGES_USE_EXT4 := true
 BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2684354560
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
index a24263d..1bae7f8 100755
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -27,6 +27,5 @@
 include build/make/target/board/BoardConfigEmuCommon.mk
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_USERIMAGES_USE_EXT4 := true
 BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2684354560 # 2.5 GB
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
diff --git a/target/product/aosp_arm.mk b/target/product/aosp_arm.mk
index 8c8f407..a052b3a 100644
--- a/target/product/aosp_arm.mk
+++ b/target/product/aosp_arm.mk
@@ -14,6 +14,13 @@
 # limitations under the License.
 #
 
+# The system image of aosp_arm-userdebug is a GSI for the devices with:
+# - ARM 32 bits user space
+# - 64 bits binder interface
+# - system-as-root
+# - VNDK enforcement
+# - compatible property override enabled
+
 PRODUCT_PROPERTY_OVERRIDES += \
 	rild.libpath=/vendor/lib/libreference-ril.so
 
@@ -27,6 +34,13 @@
 
 include $(SRC_TARGET_DIR)/product/full.mk
 
+# Enable A/B update
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS := system
+PRODUCT_PACKAGES += \
+    update_engine \
+    update_verifier
+
 # Needed by Pi newly launched device to pass VtsTrebleSysProp on GSI
 PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE := true
 
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index aeff3f0..efbc300 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -14,6 +14,13 @@
 # limitations under the License.
 #
 
+# The system image of aosp_arm64-userdebug is a GSI for the devices with:
+# - ARM 64 bits user space
+# - 64 bits binder interface
+# - system-as-root
+# - VNDK enforcement
+# - compatible property override enabled
+
 PRODUCT_PROPERTY_OVERRIDES += \
 	rild.libpath=/vendor/lib64/libreference-ril.so
 
@@ -44,6 +51,13 @@
 
 include $(SRC_TARGET_DIR)/product/emulator.mk
 
+# Enable A/B update
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS := system
+PRODUCT_PACKAGES += \
+    update_engine \
+    update_verifier
+
 # Needed by Pi newly launched device to pass VtsTrebleSysProp on GSI
 PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE := true
 
diff --git a/target/product/aosp_arm64_ab.mk b/target/product/aosp_arm64_ab.mk
index c96cb91..d389c74 100644
--- a/target/product/aosp_arm64_ab.mk
+++ b/target/product/aosp_arm64_ab.mk
@@ -19,8 +19,14 @@
 # on the generic system image, place them in build/make/target/board/
 # treble_system.prop.
 
+# aosp_arm64_ab-userdebug is a Legacy GSI for the devices with:
+# - ARM 64 bits user space
+# - 64 bits binder interface
+# - system-as-root
+
 include build/make/target/product/treble_common_64.mk
 
+# Enable A/B update
 AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS := system
 PRODUCT_PACKAGES += \
diff --git a/target/product/aosp_arm_ab.mk b/target/product/aosp_arm_ab.mk
index 98b2f99..5845d3b 100644
--- a/target/product/aosp_arm_ab.mk
+++ b/target/product/aosp_arm_ab.mk
@@ -19,8 +19,14 @@
 # on the generic system image, place them in build/make/target/board/
 # treble_system.prop.
 
+# aosp_arm_ab-userdebug is a Legacy GSI for the devices with:
+# - ARM 32 bits user space
+# - 32 bits binder interface
+# - system-as-root
+
 include build/make/target/product/treble_common_32.mk
 
+# Enable A/B update
 AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS := system
 PRODUCT_PACKAGES += \
diff --git a/target/product/aosp_x86.mk b/target/product/aosp_x86.mk
index b044149..7929a3e 100644
--- a/target/product/aosp_x86.mk
+++ b/target/product/aosp_x86.mk
@@ -14,6 +14,13 @@
 # limitations under the License.
 #
 
+# The system image of aosp_x86-userdebug is a GSI for the devices with:
+# - x86 32 bits user space
+# - 64 bits binder interface
+# - system-as-root
+# - VNDK enforcement
+# - compatible property override enabled
+
 PRODUCT_PROPERTY_OVERRIDES += \
 	rild.libpath=/vendor/lib/libreference-ril.so
 
@@ -28,6 +35,13 @@
 
 include $(SRC_TARGET_DIR)/product/full_x86.mk
 
+# Enable A/B update
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS := system
+PRODUCT_PACKAGES += \
+    update_engine \
+    update_verifier
+
 # Needed by Pi newly launched device to pass VtsTrebleSysProp on GSI
 PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE := true
 
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 4d2589e..5a934a9 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -14,6 +14,13 @@
 # limitations under the License.
 #
 
+# The system image of aosp_x86_64-userdebug is a GSI for the devices with:
+# - x86 64 bits user space
+# - 64 bits binder interface
+# - system-as-root
+# - VNDK enforcement
+# - compatible property override enabled
+
 PRODUCT_PROPERTY_OVERRIDES += \
 	rild.libpath=/vendor/lib64/libreference-ril.so
 
@@ -41,6 +48,13 @@
 
 include $(SRC_TARGET_DIR)/product/emulator.mk
 
+# Enable A/B update
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS := system
+PRODUCT_PACKAGES += \
+    update_engine \
+    update_verifier
+
 # Needed by Pi newly launched device to pass VtsTrebleSysProp on GSI
 PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE := true
 
diff --git a/target/product/aosp_x86_64_ab.mk b/target/product/aosp_x86_64_ab.mk
index 4590dc5..d9163d7 100644
--- a/target/product/aosp_x86_64_ab.mk
+++ b/target/product/aosp_x86_64_ab.mk
@@ -19,8 +19,14 @@
 # on the generic system image, place them in build/make/target/board/
 # treble_system.prop.
 
+# aosp_x86_64_ab-userdebug is a Legacy GSI for the devices with:
+# - x86 64 bits user space
+# - 64 bits binder interface
+# - system-as-root
+
 include build/make/target/product/treble_common_64.mk
 
+# Enable A/B update
 AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS := system
 PRODUCT_PACKAGES += \
diff --git a/target/product/aosp_x86_ab.mk b/target/product/aosp_x86_ab.mk
index 404a4da..4fff3d1 100644
--- a/target/product/aosp_x86_ab.mk
+++ b/target/product/aosp_x86_ab.mk
@@ -19,8 +19,14 @@
 # on the generic system image, place them in build/make/target/board/
 # treble_system.prop.
 
+# aosp_x86_ab-userdebug is a Legacy GSI for the devices with:
+# - x86 32 bits user space
+# - 32 bits binder interface
+# - system-as-root
+
 include build/make/target/product/treble_common_32.mk
 
+# Enable A/B update
 AB_OTA_UPDATER := true
 AB_OTA_PARTITIONS := system
 PRODUCT_PACKAGES += \
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 3e5dd52..6a97182 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -358,29 +358,26 @@
   img.Write()
 
 
-def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir):
-  if not img_path:
-    return
+def AppendVBMetaArgsForPartition(cmd, partition, image):
+  """Appends the VBMeta arguments for partition.
 
+  It sets up the VBMeta argument by including the partition descriptor from the
+  given 'image', or by configuring the partition as a chained partition.
+
+  Args:
+    cmd: A list of command args that will be used to generate the vbmeta image.
+        The argument for the partition will be appended to the list.
+    partition: The name of the partition (e.g. "system").
+    image: The path to the partition image.
+  """
   # Check if chain partition is used.
   key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
   if key_path:
-    # extract public key in AVB format to be included in vbmeta.img
-    avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
-    public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition)
-    p = common.Run([avbtool, "extract_public_key", "--key", key_path,
-                    "--output", public_key_path],
-                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    p.communicate()
-    assert p.returncode == 0, \
-        "avbtool extract_public_key fail for partition: %r" % partition
-
-    rollback_index_location = OPTIONS.info_dict[
-        "avb_" + partition + "_rollback_index_location"]
-    cmd.extend(["--chain_partition", "%s:%s:%s" % (
-        partition, rollback_index_location, public_key_path)])
+    chained_partition_arg = common.GetAvbChainedPartitionArg(
+        partition, OPTIONS.info_dict)
+    cmd.extend(["--chain_partition", chained_partition_arg])
   else:
-    cmd.extend(["--include_descriptors_from_image", img_path])
+    cmd.extend(["--include_descriptors_from_image", image])
 
 
 def AddVBMeta(output_zip, partitions):
@@ -389,8 +386,8 @@
   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, which include 'boot',
-        'recovery', 'system', 'vendor', 'dtbo'.
+        values. Only valid partition names are accepted, as listed in
+        common.AVB_PARTITIONS.
   """
   img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vbmeta.img")
   if os.path.exists(img.input_name):
@@ -401,13 +398,12 @@
   cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
   common.AppendAVBSigningArgs(cmd, "vbmeta")
 
-  public_key_dir = common.MakeTempDir(prefix="avbpubkey-")
   for partition, path in partitions.items():
-    assert partition in common.AVB_PARTITIONS, 'Unknown partition: %s' % (
-        partition,)
-    assert os.path.exists(path), 'Failed to find %s for partition %s' % (
-        path, partition)
-    AppendVBMetaArgsForPartition(cmd, partition, path, public_key_dir)
+    assert partition in common.AVB_PARTITIONS, \
+        'Unknown partition: {}'.format(partition)
+    assert os.path.exists(path), \
+        'Failed to find {} for {}'.format(path, partition)
+    AppendVBMetaArgsForPartition(cmd, partition, path)
 
   args = OPTIONS.info_dict.get("avb_vbmeta_args")
   if args and args.strip():
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index e82e66a..c7d93d3 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -163,7 +163,7 @@
 
   def RangeSha1(self, ranges):
     h = sha1()
-    for data in self._GetRangeData(ranges):
+    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable
       h.update(data)
     return h.hexdigest()
 
@@ -177,7 +177,7 @@
       return sha1(self.data).hexdigest()
 
   def WriteRangeDataToFd(self, ranges, fd):
-    for data in self._GetRangeData(ranges):
+    for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable
       fd.write(data)
 
 
@@ -320,46 +320,45 @@
       print(''.join(['  {}\n'.format(name) for name in values]))
 
 
-# BlockImageDiff works on two image objects.  An image object is
-# anything that provides the following attributes:
-#
-#    blocksize: the size in bytes of a block, currently must be 4096.
-#
-#    total_blocks: the total size of the partition/image, in blocks.
-#
-#    care_map: a RangeSet containing which blocks (in the range [0,
-#      total_blocks) we actually care about; i.e. which blocks contain
-#      data.
-#
-#    file_map: a dict that partitions the blocks contained in care_map
-#      into smaller domains that are useful for doing diffs on.
-#      (Typically a domain is a file, and the key in file_map is the
-#      pathname.)
-#
-#    clobbered_blocks: a RangeSet containing which blocks contain data
-#      but may be altered by the FS. They need to be excluded when
-#      verifying the partition integrity.
-#
-#    ReadRangeSet(): a function that takes a RangeSet and returns the
-#      data contained in the image blocks of that RangeSet.  The data
-#      is returned as a list or tuple of strings; concatenating the
-#      elements together should produce the requested data.
-#      Implementations are free to break up the data into list/tuple
-#      elements in any way that is convenient.
-#
-#    RangeSha1(): a function that returns (as a hex string) the SHA-1
-#      hash of all the data in the specified range.
-#
-#    TotalSha1(): a function that returns (as a hex string) the SHA-1
-#      hash of all the data in the image (ie, all the blocks in the
-#      care_map minus clobbered_blocks, or including the clobbered
-#      blocks if include_clobbered_blocks is True).
-#
-# When creating a BlockImageDiff, the src image may be None, in which
-# case the list of transfers produced will never read from the
-# original image.
-
 class BlockImageDiff(object):
+  """Generates the diff of two block image objects.
+
+  BlockImageDiff works on two image objects. An image object is anything that
+  provides the following attributes:
+
+     blocksize: the size in bytes of a block, currently must be 4096.
+
+     total_blocks: the total size of the partition/image, in blocks.
+
+     care_map: a RangeSet containing which blocks (in the range [0,
+       total_blocks) we actually care about; i.e. which blocks contain data.
+
+     file_map: a dict that partitions the blocks contained in care_map into
+         smaller domains that are useful for doing diffs on. (Typically a domain
+         is a file, and the key in file_map is the pathname.)
+
+     clobbered_blocks: a RangeSet containing which blocks contain data but may
+         be altered by the FS. They need to be excluded when verifying the
+         partition integrity.
+
+     ReadRangeSet(): a function that takes a RangeSet and returns the data
+         contained in the image blocks of that RangeSet. The data is returned as
+         a list or tuple of strings; concatenating the elements together should
+         produce the requested data. Implementations are free to break up the
+         data into list/tuple elements in any way that is convenient.
+
+     RangeSha1(): a function that returns (as a hex string) the SHA-1 hash of
+         all the data in the specified range.
+
+     TotalSha1(): a function that returns (as a hex string) the SHA-1 hash of
+         all the data in the image (ie, all the blocks in the care_map minus
+         clobbered_blocks, or including the clobbered blocks if
+         include_clobbered_blocks is True).
+
+  When creating a BlockImageDiff, the src image may be None, in which case the
+  list of transfers produced will never read from the original image.
+  """
+
   def __init__(self, tgt, src=None, threads=None, version=4,
                disable_imgdiff=False):
     if threads is None:
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index d0c9d09..b7e114d 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -425,6 +425,52 @@
   return base_fs_file if exit_code == 0 else None
 
 
+def SetUpInDirAndFsConfig(origin_in, prop_dict):
+  """Returns the in_dir and fs_config that should be used for image building.
+
+  If the target uses system_root_image and it's building system.img, it creates
+  and returns a staged dir that combines the contents of /system (i.e. in the
+  given in_dir) and root.
+
+  Args:
+    origin_in: Path to the input directory.
+    prop_dict: A property dict that contains info like partition size. Values
+        may be updated.
+
+  Returns:
+    A tuple of in_dir and fs_config that should be used to build the image.
+  """
+  fs_config = prop_dict.get("fs_config")
+  if (prop_dict.get("system_root_image") != "true" or
+      prop_dict["mount_point"] != "system"):
+    return origin_in, fs_config
+
+  # Construct a staging directory of the root file system.
+  in_dir = common.MakeTempDir()
+  root_dir = prop_dict.get("root_dir")
+  if root_dir:
+    shutil.rmtree(in_dir)
+    shutil.copytree(root_dir, in_dir, symlinks=True)
+  in_dir_system = os.path.join(in_dir, "system")
+  shutil.rmtree(in_dir_system, ignore_errors=True)
+  shutil.copytree(origin_in, in_dir_system, symlinks=True)
+
+  # Change the mount point to "/".
+  prop_dict["mount_point"] = "/"
+  if fs_config:
+    # We need to merge the fs_config files of system and root.
+    merged_fs_config = common.MakeTempFile(
+        prefix="merged_fs_config", suffix=".txt")
+    with open(merged_fs_config, "w") as fw:
+      if "root_fs_config" in prop_dict:
+        with open(prop_dict["root_fs_config"]) as fr:
+          fw.writelines(fr.readlines())
+      with open(fs_config) as fr:
+        fw.writelines(fr.readlines())
+    fs_config = merged_fs_config
+  return in_dir, fs_config
+
+
 def CheckHeadroom(ext4fs_output, prop_dict):
   """Checks if there's enough headroom space available.
 
@@ -468,40 +514,25 @@
 
 
 def BuildImage(in_dir, prop_dict, out_file, target_out=None):
-  """Build an image to out_file from in_dir with property prop_dict.
-  After the function call, values in prop_dict is updated with
-  computed values.
+  """Builds an image for the files under in_dir and writes it to out_file.
+
+  When using system_root_image, it will additionally look for the files under
+  root (specified by 'root_dir') and builds an image that contains both sources.
 
   Args:
-    in_dir: path of input directory.
-    prop_dict: property dictionary.
-    out_file: path of the output image file.
-    target_out: path of the product out directory to read device specific FS
-        config files.
+    in_dir: Path to input directory.
+    prop_dict: A property dict that contains info like partition size. Values
+        will be updated with computed values.
+    out_file: The output image file.
+    target_out: Path to the TARGET_OUT directory as in Makefile. It actually
+        points to the /system directory under PRODUCT_OUT. fs_config (the one
+        under system/core/libcutils) reads device specific FS config files from
+        there.
 
   Returns:
     True iff the image is built successfully.
   """
-  # system_root_image=true: build a system.img that combines the contents of
-  # /system and the ramdisk, and can be mounted at the root of the file system.
-  origin_in = in_dir
-  fs_config = prop_dict.get("fs_config")
-  if (prop_dict.get("system_root_image") == "true" and
-      prop_dict["mount_point"] == "system"):
-    in_dir = common.MakeTempDir()
-    # Change the mount point to "/".
-    prop_dict["mount_point"] = "/"
-    if fs_config:
-      # We need to merge the fs_config files of system and ramdisk.
-      merged_fs_config = common.MakeTempFile(prefix="root_fs_config",
-                                             suffix=".txt")
-      with open(merged_fs_config, "w") as fw:
-        if "ramdisk_fs_config" in prop_dict:
-          with open(prop_dict["ramdisk_fs_config"]) as fr:
-            fw.writelines(fr.readlines())
-        with open(fs_config) as fr:
-          fw.writelines(fr.readlines())
-      fs_config = merged_fs_config
+  in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
 
   build_command = []
   fs_type = prop_dict.get("fs_type", "")
@@ -518,11 +549,11 @@
   if (prop_dict.get("use_logical_partitions") == "true" and
       "partition_size" not in prop_dict):
     # if partition_size is not defined, use output of `du' + reserved_size
-    success, size = GetDiskUsage(origin_in)
+    success, size = GetDiskUsage(in_dir)
     if not success:
       return False
     if OPTIONS.verbose:
-      print("The tree size of %s is %d MB." % (origin_in, size // BYTES_IN_MB))
+      print("The tree size of %s is %d MB." % (in_dir, size // BYTES_IN_MB))
     size += int(prop_dict.get("partition_reserved_size", 0))
     # Round this up to a multiple of 4K so that avbtool works
     size = common.RoundUpTo4K(size)
@@ -643,31 +674,31 @@
     print("Error: unknown filesystem type '%s'" % (fs_type))
     return False
 
-  if in_dir != origin_in:
-    # Construct a staging directory of the root file system.
-    ramdisk_dir = prop_dict.get("ramdisk_dir")
-    if ramdisk_dir:
-      shutil.rmtree(in_dir)
-      shutil.copytree(ramdisk_dir, in_dir, symlinks=True)
-    staging_system = os.path.join(in_dir, "system")
-    shutil.rmtree(staging_system, ignore_errors=True)
-    shutil.copytree(origin_in, staging_system, symlinks=True)
-
   (mkfs_output, exit_code) = RunCommand(build_command)
   if exit_code != 0:
     print("Error: '%s' failed with exit code %d:\n%s" % (
         build_command, exit_code, mkfs_output))
-    success, du = GetDiskUsage(origin_in)
+    success, du = GetDiskUsage(in_dir)
     du_str = ("%d bytes (%d MB)" % (du, du // BYTES_IN_MB)
              ) if success else "unknown"
-    print("Out of space? The tree size of %s is %s." % (
-        origin_in, du_str))
-    print("The max is %d bytes (%d MB)." % (
-        int(prop_dict["partition_size"]),
-        int(prop_dict["partition_size"]) // BYTES_IN_MB))
-    print("Reserved space is %d bytes (%d MB)." % (
-        int(prop_dict.get("partition_reserved_size", 0)),
-        int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
+    print(
+        "Out of space? The tree size of {} is {}, with reserved space of {} "
+        "bytes ({} MB).".format(
+            in_dir, du_str,
+            int(prop_dict.get("partition_reserved_size", 0)),
+            int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
+    if "original_partition_size" in prop_dict:
+      print(
+          "The max size for filsystem files is {} bytes ({} MB), out of a "
+          "total image size of {} bytes ({} MB).".format(
+              int(prop_dict["partition_size"]),
+              int(prop_dict["partition_size"]) // BYTES_IN_MB,
+              int(prop_dict["original_partition_size"]),
+              int(prop_dict["original_partition_size"]) // BYTES_IN_MB))
+    else:
+      print("The max image size is {} bytes ({} MB).".format(
+          int(prop_dict["partition_size"]),
+          int(prop_dict["partition_size"]) // BYTES_IN_MB))
     return False
 
   # Check if there's enough headroom space available for ext4 image.
@@ -790,8 +821,8 @@
       d["journal_size"] = "0"
     copy_prop("system_verity_block_device", "verity_block_device")
     copy_prop("system_root_image", "system_root_image")
-    copy_prop("ramdisk_dir", "ramdisk_dir")
-    copy_prop("ramdisk_fs_config", "ramdisk_fs_config")
+    copy_prop("root_dir", "root_dir")
+    copy_prop("root_fs_config", "root_fs_config")
     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
     copy_prop("system_squashfs_compressor", "squashfs_compressor")
     copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
@@ -875,6 +906,7 @@
     copy_prop("product_extfs_inode_count", "extfs_inode_count")
     if not copy_prop("product_extfs_rsv_pct", "extfs_rsv_pct"):
       d["extfs_rsv_pct"] = "0"
+    copy_prop("product_reserved_size", "partition_reserved_size")
   elif mount_point == "product-services":
     copy_prop("avb_productservices_hashtree_enable", "avb_hashtree_enable")
     copy_prop("avb_productservices_add_hashtree_footer_args",
@@ -896,6 +928,7 @@
     copy_prop("productservices_extfs_inode_count", "extfs_inode_count")
     if not copy_prop("productservices_extfs_rsv_pct", "extfs_rsv_pct"):
       d["extfs_rsv_pct"] = "0"
+    copy_prop("productservices_reserved_size", "partition_reserved_size")
   elif mount_point == "oem":
     copy_prop("fs_type", "fs_type")
     copy_prop("oem_size", "partition_size")
@@ -929,12 +962,22 @@
       d[dest_p] = image_prop[src_p]
       return True
     return False
+
+  if "original_partition_size" in image_prop:
+    size_property = "original_partition_size"
+  else:
+    size_property = "partition_size"
+
   if mount_point == "system":
-    copy_prop("partition_size", "system_size")
+    copy_prop(size_property, "system_size")
   elif mount_point == "system_other":
-    copy_prop("partition_size", "system_size")
+    copy_prop(size_property, "system_size")
   elif mount_point == "vendor":
-    copy_prop("partition_size", "vendor_size")
+    copy_prop(size_property, "vendor_size")
+  elif mount_point == "product":
+    copy_prop(size_property, "product_size")
+  elif mount_point == "product-services":
+    copy_prop(size_property, "productservices_size")
   return d
 
 
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 0d8f065..c10e57c 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -44,7 +44,7 @@
         "darwin": "out/host/darwin-x86",
     }
 
-    self.search_path = platform_search_path.get(sys.platform, None)
+    self.search_path = platform_search_path.get(sys.platform)
     self.signapk_path = "framework/signapk.jar"  # Relative to search_path
     self.signapk_shared_library_path = "lib64"   # Relative to search_path
     self.extra_signapk_args = []
@@ -196,10 +196,10 @@
     if fc_config:
       d["selinux_fc"] = fc_config
 
-    # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
+    # Similarly we need to redirect "root_dir" and "root_fs_config".
     if d.get("system_root_image") == "true":
-      d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
-      d["ramdisk_fs_config"] = os.path.join(
+      d["root_dir"] = os.path.join(input_dir, "ROOT")
+      d["root_fs_config"] = os.path.join(
           input_dir, "META", "root_filesystem_config.txt")
 
     # Redirect {system,vendor}_base_fs_file.
@@ -237,15 +237,15 @@
   makeint("boot_size")
   makeint("fstab_version")
 
-  system_root_image = d.get("system_root_image", None) == "true"
-  if d.get("no_recovery", None) != "true":
+  system_root_image = d.get("system_root_image") == "true"
+  if d.get("no_recovery") != "true":
     recovery_fstab_path = "RECOVERY/RAMDISK/etc/recovery.fstab"
-    d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
-        recovery_fstab_path, system_root_image)
-  elif d.get("recovery_as_boot", None) == "true":
+    d["fstab"] = LoadRecoveryFSTab(
+        read_helper, d["fstab_version"], recovery_fstab_path, system_root_image)
+  elif d.get("recovery_as_boot") == "true":
     recovery_fstab_path = "BOOT/RAMDISK/etc/recovery.fstab"
-    d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
-        recovery_fstab_path, system_root_image)
+    d["fstab"] = LoadRecoveryFSTab(
+        read_helper, d["fstab_version"], recovery_fstab_path, system_root_image)
   else:
     d["fstab"] = None
 
@@ -371,6 +371,40 @@
     cmd.extend(["--salt", avb_salt])
 
 
+def GetAvbChainedPartitionArg(partition, info_dict, key=None):
+  """Constructs and returns the arg to build or verify a chained partition.
+
+  Args:
+    partition: The partition name.
+    info_dict: The info dict to look up the key info and rollback index
+        location.
+    key: The key to be used for building or verifying the partition. Defaults to
+        the key listed in info_dict.
+
+  Returns:
+    A string of form "partition:rollback_index_location:key" that can be used to
+    build or verify vbmeta image.
+
+  Raises:
+    AssertionError: When it fails to extract the public key with avbtool.
+  """
+  if key is None:
+    key = info_dict["avb_" + partition + "_key_path"]
+  avbtool = os.getenv('AVBTOOL') or info_dict["avb_avbtool"]
+  pubkey_path = MakeTempFile(prefix="avb-", suffix=".pubkey")
+  proc = Run(
+      [avbtool, "extract_public_key", "--key", key, "--output", pubkey_path],
+      stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  stdoutdata, _ = proc.communicate()
+  assert proc.returncode == 0, \
+      "Failed to extract pubkey for {}:\n{}".format(
+          partition, stdoutdata)
+
+  rollback_index_location = info_dict[
+      "avb_" + partition + "_rollback_index_location"]
+  return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
+
+
 def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
                         has_ramdisk=False, two_step_image=False):
   """Build a bootable image from the specified sourcedir.
@@ -441,11 +475,11 @@
     cmd.append("--pagesize")
     cmd.append(open(fn).read().rstrip("\n"))
 
-  args = info_dict.get("mkbootimg_args", None)
+  args = info_dict.get("mkbootimg_args")
   if args and args.strip():
     cmd.extend(shlex.split(args))
 
-  args = info_dict.get("mkbootimg_version_args", None)
+  args = info_dict.get("mkbootimg_version_args")
   if args and args.strip():
     cmd.extend(shlex.split(args))
 
@@ -453,7 +487,7 @@
     cmd.extend(["--ramdisk", ramdisk_img.name])
 
   img_unsigned = None
-  if info_dict.get("vboot", None):
+  if info_dict.get("vboot"):
     img_unsigned = tempfile.NamedTemporaryFile()
     cmd.extend(["--output", img_unsigned.name])
   else:
@@ -471,8 +505,8 @@
   p.communicate()
   assert p.returncode == 0, "mkbootimg of %s image failed" % (partition_name,)
 
-  if (info_dict.get("boot_signer", None) == "true" and
-      info_dict.get("verity_key", None)):
+  if (info_dict.get("boot_signer") == "true" and
+      info_dict.get("verity_key")):
     # Hard-code the path as "/boot" for two-step special recovery image (which
     # will be loaded into /boot during the two-step OTA).
     if two_step_image:
@@ -489,7 +523,7 @@
     assert p.returncode == 0, "boot_signer of %s image failed" % path
 
   # Sign the image if vboot is non-empty.
-  elif info_dict.get("vboot", None):
+  elif info_dict.get("vboot"):
     path = "/" + partition_name
     img_keyblock = tempfile.NamedTemporaryFile()
     # We have switched from the prebuilt futility binary to using the tool
@@ -578,9 +612,9 @@
 
 
 def Gunzip(in_filename, out_filename):
-  """Gunzip the given gzip compressed file to a given output file.
-  """
-  with gzip.open(in_filename, "rb") as in_file, open(out_filename, "wb") as out_file:
+  """Gunzips the given gzip compressed file to a given output file."""
+  with gzip.open(in_filename, "rb") as in_file, \
+       open(out_filename, "wb") as out_file:
     shutil.copyfileobj(in_file, out_file)
 
 
@@ -734,7 +768,7 @@
   devnull.close()
 
   key_passwords.update(PasswordManager().GetPasswords(need_passwords))
-  key_passwords.update(dict.fromkeys(no_passwords, None))
+  key_passwords.update(dict.fromkeys(no_passwords))
   return key_passwords
 
 
@@ -799,8 +833,7 @@
 
 
 def SignFile(input_name, output_name, key, password, min_api_level=None,
-    codename_to_api_level_map=dict(),
-    whole_file=False):
+             codename_to_api_level_map=None, whole_file=False):
   """Sign the input_name zip/jar/apk, producing output_name.  Use the
   given key and password (the latter may be None if the key does not
   have a password.
@@ -816,6 +849,8 @@
   codename_to_api_level_map is needed to translate the codename which may be
   encountered as the APK's minSdkVersion.
   """
+  if codename_to_api_level_map is None:
+    codename_to_api_level_map = {}
 
   java_library_path = os.path.join(
       OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
@@ -877,7 +912,7 @@
     device = p.device
     if "/" in device:
       device = device[device.rfind("/")+1:]
-    limit = info_dict.get(device + "_size", None)
+    limit = info_dict.get(device + "_size")
   if not fs_type or not limit:
     return
 
@@ -1107,8 +1142,8 @@
 
 class PasswordManager(object):
   def __init__(self):
-    self.editor = os.getenv("EDITOR", None)
-    self.pwfile = os.getenv("ANDROID_PW_FILE", None)
+    self.editor = os.getenv("EDITOR")
+    self.pwfile = os.getenv("ANDROID_PW_FILE")
 
   def GetPasswords(self, items):
     """Get passwords corresponding to each string in 'items',
@@ -1358,7 +1393,7 @@
     module does not define the function, return the value of the
     'default' kwarg (which itself defaults to None)."""
     if self.module is None or not hasattr(self.module, function_name):
-      return kwargs.get("default", None)
+      return kwargs.get("default")
     return getattr(self.module, function_name)(*((self,) + args), **kwargs)
 
   def FullOTA_Assertions(self):
@@ -1408,8 +1443,9 @@
   def VerifyOTA_Assertions(self):
     return self._DoCall("VerifyOTA_Assertions")
 
+
 class File(object):
-  def __init__(self, name, data, compress_size = None):
+  def __init__(self, name, data, compress_size=None):
     self.name = name
     self.data = data
     self.size = len(data)
@@ -1436,6 +1472,7 @@
   def AddToZip(self, z, compression=None):
     ZipWriteStr(z, self.name, self.data, compress_type=compression)
 
+
 DIFF_PROGRAM_BY_EXT = {
     ".gz" : "imgdiff",
     ".zip" : ["imgdiff", "-z"],
@@ -1444,6 +1481,7 @@
     ".img" : "imgdiff",
     }
 
+
 class Difference(object):
   def __init__(self, tf, sf, diff_program=None):
     self.tf = tf
@@ -1511,9 +1549,11 @@
 
 
   def GetPatch(self):
-    """Return a tuple (target_file, source_file, patch_data).
+    """Returns a tuple of (target_file, source_file, patch_data).
+
     patch_data may be None if ComputePatch hasn't been called, or if
-    computing the patch failed."""
+    computing the patch failed.
+    """
     return self.tf, self.sf, self.patch
 
 
@@ -1545,7 +1585,8 @@
         else:
           name = "%s (%s)" % (tf.name, sf.name)
         if patch is None:
-          print("patching failed!                                  %s" % (name,))
+          print(
+              "patching failed!                                  %s" % (name,))
         else:
           print("%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
               dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name))
@@ -1598,7 +1639,8 @@
   def required_cache(self):
     return self._required_cache
 
-  def WriteScript(self, script, output_zip, progress=None):
+  def WriteScript(self, script, output_zip, progress=None,
+                  write_verify_script=False):
     if not self.src:
       # write the output unconditionally
       script.Print("Patching %s image unconditionally..." % (self.partition,))
@@ -1608,7 +1650,8 @@
     if progress:
       script.ShowProgress(progress, 0)
     self._WriteUpdate(script, output_zip)
-    if OPTIONS.verify:
+
+    if write_verify_script:
       self._WritePostInstallVerifyScript(script)
 
   def WriteStrictVerifyScript(self, script):
@@ -1622,12 +1665,12 @@
     script.Print("Verifying %s..." % (partition,))
     ranges = self.tgt.care_map
     ranges_str = ranges.to_string_raw()
-    script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
-                       'ui_print("    Verified.") || '
-                       'ui_print("\\"%s\\" has unexpected contents.");' % (
-                       self.device, ranges_str,
-                       self.tgt.TotalSha1(include_clobbered_blocks=True),
-                       self.device))
+    script.AppendExtra(
+        'range_sha1("%s", "%s") == "%s" && ui_print("    Verified.") || '
+        'ui_print("\\"%s\\" has unexpected contents.");' % (
+            self.device, ranges_str,
+            self.tgt.TotalSha1(include_clobbered_blocks=True),
+            self.device))
     script.AppendExtra("")
 
   def WriteVerifyScript(self, script, touched_blocks_only=False):
@@ -1651,12 +1694,12 @@
         return
 
       ranges_str = ranges.to_string_raw()
-      script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
-                          'block_image_verify("%s", '
-                          'package_extract_file("%s.transfer.list"), '
-                          '"%s.new.dat", "%s.patch.dat")) then') % (
-                          self.device, ranges_str, expected_sha1,
-                          self.device, partition, partition, partition))
+      script.AppendExtra(
+          'if (range_sha1("%s", "%s") == "%s" || block_image_verify("%s", '
+          'package_extract_file("%s.transfer.list"), "%s.new.dat", '
+          '"%s.patch.dat")) then' % (
+              self.device, ranges_str, expected_sha1,
+              self.device, partition, partition, partition))
       script.Print('Verified %s image...' % (partition,))
       script.AppendExtra('else')
 
@@ -1706,17 +1749,19 @@
     # Unlike pre-install verification, clobbered_blocks should not be ignored.
     ranges = self.tgt.care_map
     ranges_str = ranges.to_string_raw()
-    script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
-                       self.device, ranges_str,
-                       self.tgt.TotalSha1(include_clobbered_blocks=True)))
+    script.AppendExtra(
+        'if range_sha1("%s", "%s") == "%s" then' % (
+            self.device, ranges_str,
+            self.tgt.TotalSha1(include_clobbered_blocks=True)))
 
     # Bug: 20881595
     # Verify that extended blocks are really zeroed out.
     if self.tgt.extended:
       ranges_str = self.tgt.extended.to_string_raw()
-      script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
-                         self.device, ranges_str,
-                         self._HashZeroBlocks(self.tgt.extended.size())))
+      script.AppendExtra(
+          'if range_sha1("%s", "%s") == "%s" then' % (
+              self.device, ranges_str,
+              self._HashZeroBlocks(self.tgt.extended.size())))
       script.Print('Verified the updated %s image.' % (partition,))
       if partition == "system":
         code = ErrorCode.SYSTEM_NONZERO_CONTENTS
@@ -1746,9 +1791,9 @@
              '{}.transfer.list'.format(self.path),
              '{}.transfer.list'.format(self.partition))
 
-    # For full OTA, compress the new.dat with brotli with quality 6 to reduce its size. Quailty 9
-    # almost triples the compression time but doesn't further reduce the size too much.
-    # For a typical 1.8G system.new.dat
+    # For full OTA, compress the new.dat with brotli with quality 6 to reduce
+    # its size. Quailty 9 almost triples the compression time but doesn't
+    # further reduce the size too much. For a typical 1.8G system.new.dat
     #                       zip  | brotli(quality 6)  | brotli(quality 9)
     #   compressed_size:    942M | 869M (~8% reduced) | 854M
     #   compression_time:   75s  | 265s               | 719s
@@ -1813,6 +1858,7 @@
 
 DataImage = blockimgdiff.DataImage
 
+
 # map recovery.fstab's fs_types to mount/format "partition types"
 PARTITION_TYPES = {
     "ext4": "EMMC",
@@ -1821,6 +1867,7 @@
     "squashfs": "EMMC"
 }
 
+
 def GetTypeAndDevice(mount_point, info):
   fstab = info["fstab"]
   if fstab:
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 5c3533e..3595a9e 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -132,8 +132,8 @@
     self.script.append(
         ('(!less_than_int(%s, getprop("ro.build.date.utc"))) || '
          'abort("E%d: Can\'t install this package (%s) over newer '
-         'build (" + getprop("ro.build.date") + ").");') % (timestamp,
-             common.ErrorCode.OLDER_BUILD, timestamp_text))
+         'build (" + getprop("ro.build.date") + ").");') % (
+             timestamp, common.ErrorCode.OLDER_BUILD, timestamp_text))
 
   def AssertDevice(self, device):
     """Assert that the device identifier is the given string."""
@@ -260,8 +260,8 @@
       cmd.append(',\0%s,\0package_extract_file("%s")' % patchpairs[i:i+2])
     cmd.append(') ||\n    abort("E%d: Failed to apply patch to %s");' % (
         common.ErrorCode.APPLY_PATCH_FAILURE, srcfile))
-    cmd = "".join(cmd)
-    self.script.append(self.WordWrap(cmd))
+    cmd_str = "".join(cmd)
+    self.script.append(self.WordWrap(cmd_str))
 
   def WriteRawImage(self, mount_point, fn, mapfn=None):
     """Write the given package file into the partition for the given
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index e6e8c9f..01ff149 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -28,17 +28,17 @@
 
 from __future__ import print_function
 
+import os
+import shutil
 import sys
+import zipfile
+
+import common
 
 if sys.hexversion < 0x02070000:
   print("Python 2.7 or newer is required.", file=sys.stderr)
   sys.exit(1)
 
-import os
-import shutil
-import zipfile
-
-import common
 
 OPTIONS = common.OPTIONS
 
@@ -51,11 +51,12 @@
 
 
 def main(argv):
-  bootable_only = [False]
+  # This allows modifying the value from inner function.
+  bootable_only_array = [False]
 
   def option_handler(o, _):
     if o in ("-z", "--bootable_zip"):
-      bootable_only[0] = True
+      bootable_only_array[0] = True
     else:
       return False
     return True
@@ -65,7 +66,7 @@
                              extra_long_opts=["bootable_zip"],
                              extra_option_handler=option_handler)
 
-  bootable_only = bootable_only[0]
+  bootable_only = bootable_only_array[0]
 
   if len(args) != 2:
     common.Usage(__doc__)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index b9c410f..8cfe4c3 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -827,7 +827,8 @@
                                      allow_shared_blocks)
   system_tgt.ResetFileMap()
   system_diff = common.BlockDifference("system", system_tgt, src=None)
-  system_diff.WriteScript(script, output_zip)
+  system_diff.WriteScript(script, output_zip,
+                          write_verify_script=OPTIONS.verify)
 
   boot_img = common.GetBootableImage(
       "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
@@ -839,7 +840,8 @@
                                        allow_shared_blocks)
     vendor_tgt.ResetFileMap()
     vendor_diff = common.BlockDifference("vendor", vendor_tgt)
-    vendor_diff.WriteScript(script, output_zip)
+    vendor_diff.WriteScript(script, output_zip,
+                            write_verify_script=OPTIONS.verify)
 
   AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
 
@@ -1559,10 +1561,12 @@
   device_specific.IncrementalOTA_InstallBegin()
 
   system_diff.WriteScript(script, output_zip,
-                          progress=0.8 if vendor_diff else 0.9)
+                          progress=0.8 if vendor_diff else 0.9,
+                          write_verify_script=OPTIONS.verify)
 
   if vendor_diff:
-    vendor_diff.WriteScript(script, output_zip, progress=0.1)
+    vendor_diff.WriteScript(script, output_zip, progress=0.1,
+                            write_verify_script=OPTIONS.verify)
 
   if OPTIONS.two_step:
     common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
diff --git a/tools/releasetools/pylintrc b/tools/releasetools/pylintrc
index 7b3405c..2a30742 100644
--- a/tools/releasetools/pylintrc
+++ b/tools/releasetools/pylintrc
@@ -62,7 +62,7 @@
 # --enable=similarities". If you want to run only the classes checker, but have
 # no Warning level messages displayed, use"--disable=all --enable=classes
 # --disable=W"
-disable=invalid-name,missing-docstring,too-many-branches,too-many-locals,too-many-arguments,too-many-statements,duplicate-code,too-few-public-methods,too-many-instance-attributes,too-many-lines,too-many-public-methods,locally-disabled,fixme
+disable=invalid-name,missing-docstring,too-many-branches,too-many-locals,too-many-arguments,too-many-statements,duplicate-code,too-few-public-methods,too-many-instance-attributes,too-many-lines,too-many-public-methods,locally-disabled,fixme,not-callable
 
 
 [REPORTS]
diff --git a/tools/releasetools/test_add_img_to_target_files.py b/tools/releasetools/test_add_img_to_target_files.py
index a9c7f7c..3f2e5ea 100644
--- a/tools/releasetools/test_add_img_to_target_files.py
+++ b/tools/releasetools/test_add_img_to_target_files.py
@@ -22,7 +22,8 @@
 import common
 import test_utils
 from add_img_to_target_files import (
-    AddCareMapTxtForAbOta, AddPackRadioImages, CheckAbOtaImages, GetCareMap)
+    AddCareMapTxtForAbOta, AddPackRadioImages, AppendVBMetaArgsForPartition,
+    CheckAbOtaImages, GetCareMap)
 from rangelib import RangeSet
 
 
@@ -264,6 +265,31 @@
     # The existing entry should be scheduled to be replaced.
     self.assertIn('META/care_map.txt', OPTIONS.replace_updated_files_list)
 
+  def test_AppendVBMetaArgsForPartition(self):
+    OPTIONS.info_dict = {}
+    cmd = []
+    AppendVBMetaArgsForPartition(cmd, 'system', '/path/to/system.img')
+    self.assertEqual(
+        ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
+
+  def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
+    testdata_dir = test_utils.get_testdata_dir()
+    pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
+    OPTIONS.info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_vendor_key_path': pubkey,
+        'avb_vendor_rollback_index_location': 5,
+    }
+    cmd = []
+    AppendVBMetaArgsForPartition(cmd, 'vendor', '/path/to/vendor.img')
+    self.assertEqual(2, len(cmd))
+    self.assertEqual('--chain_partition', cmd[0])
+    chained_partition_args = cmd[1].split(':')
+    self.assertEqual(3, len(chained_partition_args))
+    self.assertEqual('vendor', chained_partition_args[0])
+    self.assertEqual('5', chained_partition_args[1])
+    self.assertTrue(os.path.exists(chained_partition_args[2]))
+
   def test_GetCareMap(self):
     sparse_image = test_utils.construct_sparse_image([
         (0xCAC1, 6),
diff --git a/tools/releasetools/test_build_image.py b/tools/releasetools/test_build_image.py
index 161faff..19b5e08 100644
--- a/tools/releasetools/test_build_image.py
+++ b/tools/releasetools/test_build_image.py
@@ -14,10 +14,12 @@
 # limitations under the License.
 #
 
+import filecmp
+import os.path
 import unittest
 
 import common
-from build_image import CheckHeadroom, RunCommand
+from build_image import CheckHeadroom, RunCommand, SetUpInDirAndFsConfig
 
 
 class BuildImageTest(unittest.TestCase):
@@ -26,6 +28,9 @@
   EXT4FS_OUTPUT = (
       "Created filesystem with 2777/129024 inodes and 515099/516099 blocks")
 
+  def tearDown(self):
+    common.Cleanup()
+
   def test_CheckHeadroom_SizeUnderLimit(self):
     # Required headroom: 1000 blocks.
     prop_dict = {
@@ -91,4 +96,95 @@
     }
     self.assertFalse(CheckHeadroom(ext4fs_output, prop_dict))
 
-    common.Cleanup()
+  def test_SetUpInDirAndFsConfig_SystemRootImageFalse(self):
+    prop_dict = {
+        'fs_config': 'fs-config',
+        'mount_point': 'system',
+    }
+    in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)
+    self.assertEqual('/path/to/in_dir', in_dir)
+    self.assertEqual('fs-config', fs_config)
+    self.assertEqual('system', prop_dict['mount_point'])
+
+  def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self):
+    prop_dict = {
+        'fs_config': 'fs-config',
+        'mount_point': 'vendor',
+        'system_root_image': 'true',
+    }
+    in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)
+    self.assertEqual('/path/to/in_dir', in_dir)
+    self.assertEqual('fs-config', fs_config)
+    self.assertEqual('vendor', prop_dict['mount_point'])
+
+  @staticmethod
+  def _gen_fs_config(partition):
+    fs_config = common.MakeTempFile(suffix='.txt')
+    with open(fs_config, 'w') as fs_config_fp:
+      fs_config_fp.write('fs-config-{}\n'.format(partition))
+    return fs_config
+
+  def test_SetUpInDirAndFsConfig_SystemRootImageTrue(self):
+    root_dir = common.MakeTempDir()
+    with open(os.path.join(root_dir, 'init'), 'w') as init_fp:
+      init_fp.write('init')
+
+    origin_in = common.MakeTempDir()
+    with open(os.path.join(origin_in, 'file'), 'w') as in_fp:
+      in_fp.write('system-file')
+    os.symlink('../etc', os.path.join(origin_in, 'symlink'))
+
+    fs_config_system = self._gen_fs_config('system')
+
+    prop_dict = {
+        'fs_config': fs_config_system,
+        'mount_point': 'system',
+        'root_dir': root_dir,
+        'system_root_image': 'true',
+    }
+    in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)
+
+    self.assertTrue(filecmp.cmp(
+        os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))
+    self.assertTrue(filecmp.cmp(
+        os.path.join(in_dir, 'system', 'file'),
+        os.path.join(origin_in, 'file')))
+    self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))
+
+    self.assertTrue(filecmp.cmp(fs_config_system, fs_config))
+    self.assertEqual('/', prop_dict['mount_point'])
+
+  def test_SetUpInDirAndFsConfig_SystemRootImageTrue_WithRootFsConfig(self):
+    root_dir = common.MakeTempDir()
+    with open(os.path.join(root_dir, 'init'), 'w') as init_fp:
+      init_fp.write('init')
+
+    origin_in = common.MakeTempDir()
+    with open(os.path.join(origin_in, 'file'), 'w') as in_fp:
+      in_fp.write('system-file')
+    os.symlink('../etc', os.path.join(origin_in, 'symlink'))
+
+    fs_config_system = self._gen_fs_config('system')
+    fs_config_root = self._gen_fs_config('root')
+
+    prop_dict = {
+        'fs_config': fs_config_system,
+        'mount_point': 'system',
+        'root_dir': root_dir,
+        'root_fs_config': fs_config_root,
+        'system_root_image': 'true',
+    }
+    in_dir, fs_config = SetUpInDirAndFsConfig(origin_in, prop_dict)
+
+    self.assertTrue(filecmp.cmp(
+        os.path.join(in_dir, 'init'), os.path.join(root_dir, 'init')))
+    self.assertTrue(filecmp.cmp(
+        os.path.join(in_dir, 'system', 'file'),
+        os.path.join(origin_in, 'file')))
+    self.assertTrue(os.path.islink(os.path.join(in_dir, 'system', 'symlink')))
+
+    with open(fs_config) as fs_config_fp:
+      fs_config_data = fs_config_fp.readlines()
+    self.assertIn('fs-config-system\n', fs_config_data)
+    self.assertIn('fs-config-root\n', fs_config_data)
+    self.assertEqual('/', prop_dict['mount_point'])
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 1c75d19..2a28db4 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -524,6 +524,9 @@
 
 class CommonUtilsTest(unittest.TestCase):
 
+  def setUp(self):
+    self.testdata_dir = test_utils.get_testdata_dir()
+
   def tearDown(self):
     common.Cleanup()
 
@@ -730,6 +733,56 @@
           AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
           False)
 
+  def test_GetAvbChainedPartitionArg(self):
+    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
+    info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_system_key_path': pubkey,
+        'avb_system_rollback_index_location': 2,
+    }
+    args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
+    self.assertEqual(3, len(args))
+    self.assertEqual('system', args[0])
+    self.assertEqual('2', args[1])
+    self.assertTrue(os.path.exists(args[2]))
+
+  def test_GetAvbChainedPartitionArg_withPrivateKey(self):
+    key = os.path.join(self.testdata_dir, 'testkey.key')
+    info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_product_key_path': key,
+        'avb_product_rollback_index_location': 2,
+    }
+    args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
+    self.assertEqual(3, len(args))
+    self.assertEqual('product', args[0])
+    self.assertEqual('2', args[1])
+    self.assertTrue(os.path.exists(args[2]))
+
+  def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
+    info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_system_key_path': 'does-not-exist',
+        'avb_system_rollback_index_location': 2,
+    }
+    pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
+    args = common.GetAvbChainedPartitionArg(
+        'system', info_dict, pubkey).split(':')
+    self.assertEqual(3, len(args))
+    self.assertEqual('system', args[0])
+    self.assertEqual('2', args[1])
+    self.assertTrue(os.path.exists(args[2]))
+
+  def test_GetAvbChainedPartitionArg_invalidKey(self):
+    pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
+    info_dict = {
+        'avb_avbtool': 'avbtool',
+        'avb_system_key_path': pubkey,
+        'avb_system_rollback_index_location': 2,
+    }
+    self.assertRaises(
+        AssertionError, common.GetAvbChainedPartitionArg, 'system', info_dict)
+
 
 class InstallRecoveryScriptFormatTest(unittest.TestCase):
   """Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/testdata/testkey.key b/tools/releasetools/testdata/testkey.key
new file mode 100644
index 0000000..3a84607
--- /dev/null
+++ b/tools/releasetools/testdata/testkey.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+O/I7YvBaCZA3
+KrvP7ErTh6DS3cAvjLY2GkAA7NWcXIICsVwWMtMZAMO+Rtk/o3XY7r53Amg8ue2e
+b0D5Wc8gUVEeDQdRZJscz5CTwmC/b/YBWQBSPknWv23hf7ZYjR5HMk/XlmOfrylA
+oaZzJyKvLSBu+Zi9cvZlSZObnoOYR8JQJEhjYgYn8JwycV4i1VTQvEqxXkyW3kE7
+RW/8JXgRqI4vDIKehm5SFi2jt0eU7/ju/8f3OGQkLng4DV2QPfwQ+A7kad+EYVI1
+dBGYkNNesWB3o75A7jJQ1fyVg/XQzOKZSki1lrTm3rw0AOrBiXdPudbO+4L2vgip
+kPI9/bVNAgMBAAECggEABGjBSY0Wgw+7rvunlL8mUNbQ7HJFVRTO2FwtZZgXr2MZ
+hFR2DPGqoOa6ortjp6zzO071TS7aGaY5krWDbQQe3+Hinm6w37sUOUu6TyJvOaCv
+tAJLFpzo+zg+pL5gDJdgv0e0QAv1TSszKpNUl1Ct5h+Go+vXFXUHrvtQl4fKBwqA
+efxcd3R4z3p/3Cl2ZYIRz9I7UXUZZYwJE7bDNDz3aFZ1jUoELGmhe1O5w0hJY1q6
+PxuOM9bL60yDn0vu0eiCjaPlHeHyGe9pQ1aQLEuwQz9zpWC01dWPVkLmny7HDygC
+VBsdg8MNlzJQ1WV2en11BH72IqZ59U8pD0xEB7a4BQKBgQDxenfXyYZw4AKcaJlP
+ncJmsx/wcgEvWNxiI4etArXES4VIyP2OlSw+q9JbOOpaSk8TJP5PNfUkgTbC4B2y
+gh/AobJP5b7Wn5LrsHc3GY6CzF1i8T4xXQRxnaKWE86SOmZQlEmyCnpyCmfFVuaR
+E8p8CPW/gQLhpxSlQdGZ0bYLiwKBgQDJrJRDdyaI/Isusog/OwKHVGBU6CmRa5tM
+gx+GIlxheqhuDqnBkr0h1kL20Zi80JeG7vKWr+dwfqkEarfdTe+juwlIuQ3MEuXL
+AbsKNuaU1naOqOLm9rjZgRtR7oNLVH5AbkKMaJz1zM6YiMl54FEDX7ZVY8b6q1Kz
+YXT3sGi9hwKBgBsNa0ujagpPLjuzhCllNRgoTRW0z+kr/VSJQnPhb9eT1lS3H6DP
+mWtT+Hb7w1VmKcGtTUg2dUYnq6jdTrZm2YPNGZrV1DFbIyyAUnq7xDlnB7dD64HA
+N/U6gbJqeaPsIvY4BqGJhvorrEBxYdcy7mZC4rUXkOkSvL9exkqDMe/NAoGARaHU
+v0aQg5PO6pyx9kMFqHw1lptiXtdsk4pihAmxI+cZ6IYfjrp/mwNDs7zCo87RwsEV
++Xlay7iv2tqOCVczerDFj9p1LRUJSoKadfhmvNUfsjoVvfFJ+a9eI3fa1VOjE9P+
+HkSwjR3d50Sza+VLk4Kkje8ZcMtejpkDrdG3GFkCgYBXHqciwlFn5nMPFRe8v426
+6YBiUtzCQCZxDtMeeZYCJslFfjrqPXNUcU/flxWwaikjFsLJEtl7aT3Hpdi5I7T8
+yCYkUWqAAh7twEYTOeG6v/tEa/PmsBjZXPD2zkCp76EQmv3gbvsH4F/nA55gT/GR
+2in6XS/4rHBvjn5gF6MFyg==
+-----END PRIVATE KEY-----
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 4e40b86..09f800f 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -315,9 +315,19 @@
     key = options['verity_key']
     if key is None:
       key = info_dict['avb_vbmeta_key_path']
+
     # avbtool verifies all the images that have descriptors listed in vbmeta.
     image = os.path.join(input_tmp, 'IMAGES', 'vbmeta.img')
     cmd = ['avbtool', 'verify_image', '--image', image, '--key', key]
+
+    # Append the args for chained partitions if any.
+    for partition in common.AVB_PARTITIONS:
+      key_name = 'avb_' + partition + '_key_path'
+      if info_dict.get(key_name) is not None:
+        chained_partition_arg = common.GetAvbChainedPartitionArg(
+            partition, info_dict, options[key_name])
+        cmd.extend(["--expected_chain_partition", chained_partition_arg])
+
     proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     stdoutdata, _ = proc.communicate()
     assert proc.returncode == 0, \
@@ -339,8 +349,13 @@
   parser.add_argument(
       '--verity_key',
       help='the verity public key to verify the bootable images (Verified '
-           'Boot 1.0), or the vbmeta image (Verified Boot 2.0), where '
+           'Boot 1.0), or the vbmeta image (Verified Boot 2.0, aka AVB), where '
            'applicable')
+  for partition in common.AVB_PARTITIONS:
+    parser.add_argument(
+        '--avb_' + partition + '_key_path',
+        help='the public or private key in PEM format to verify AVB chained '
+             'partition of {}'.format(partition))
   parser.add_argument(
       '--verity_key_mincrypt',
       help='the verity public key in mincrypt format to verify the system '