Merge "Add zip2zip as a required module of releasetools_common." into main
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 4a7e957..dfc0cd0 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -776,6 +776,9 @@
 # Clear out tools/metalava Bazel output dir
 $(call add-clean-step, rm -rf $(OUT_DIR)/bazel/output/execroot/__main__/bazel-out/mixed_builds_product-*/bin/tools/metalava)
 
+# Clear out rustc compiler intermediates after reverting rust compiler/linker split.
+$(call add-clean-step, find $(OUT_DIR) -name "*.rsp.whole.a" -print0 | xargs -0 /bin/bash -c 'rm -f $$$${@}; rm -f $$$${@/.rsp.whole.a/.rsp.a}; rm -f $$$${@/.rsp.whole.a/.rsp}')
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index fc2a132..e77b8e7 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1233,6 +1233,7 @@
   $(AVBTOOL) add_hash_footer \
           --image $(1) \
           $(call get-partition-size-argument,$(call get-bootimage-partition-size,$(1),boot)) \
+          --salt `sha256sum "$(kernel)" | cut -d " " -f 1` \
           --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) \
           $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
 endef
diff --git a/core/config.mk b/core/config.mk
index c339590..2f457de 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1303,9 +1303,8 @@
 include $(BUILD_SYSTEM)/soong_config.mk
 endif
 
--include external/linux-kselftest/android/kselftest_test_list.mk
 -include external/ltp/android/ltp_package_list.mk
-DEFAULT_DATA_OUT_MODULES := ltp $(ltp_packages) $(kselftest_modules)
+DEFAULT_DATA_OUT_MODULES := ltp $(ltp_packages)
 .KATI_READONLY := DEFAULT_DATA_OUT_MODULES
 
 include $(BUILD_SYSTEM)/dumpvar.mk
diff --git a/core/envsetup.mk b/core/envsetup.mk
index f5a2022..091c2e3 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -720,6 +720,7 @@
 TARGET_OUT_VENDOR_APPS := $(target_out_vendor_app_base)/app
 TARGET_OUT_VENDOR_APPS_PRIVILEGED := $(target_out_vendor_app_base)/priv-app
 TARGET_OUT_VENDOR_ETC := $(TARGET_OUT_VENDOR)/etc
+TARGET_OUT_VENDOR_FAKE := $(PRODUCT_OUT)/vendor_fake_packages
 .KATI_READONLY := \
   TARGET_OUT_VENDOR_EXECUTABLES \
   TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES \
@@ -728,7 +729,8 @@
   TARGET_OUT_VENDOR_JAVA_LIBRARIES \
   TARGET_OUT_VENDOR_APPS \
   TARGET_OUT_VENDOR_APPS_PRIVILEGED \
-  TARGET_OUT_VENDOR_ETC
+  TARGET_OUT_VENDOR_ETC \
+  TARGET_OUT_VENDOR_FAKE
 
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_EXECUTABLES := $(TARGET_OUT_VENDOR_EXECUTABLES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_VENDOR_SHARED_LIBRARIES := $(target_out_vendor_shared_libraries_base)/lib
diff --git a/core/soong_config.mk b/core/soong_config.mk
index b172ec2..90b47a0 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -40,7 +40,9 @@
 $(call add_json_str,  Platform_base_os,                  $(PLATFORM_BASE_OS))
 $(call add_json_str,  Platform_version_last_stable,      $(PLATFORM_VERSION_LAST_STABLE))
 $(call add_json_str,  Platform_version_known_codenames,  $(PLATFORM_VERSION_KNOWN_CODENAMES))
+
 $(call add_json_bool, Release_aidl_use_unfrozen,         $(RELEASE_AIDL_USE_UNFROZEN))
+$(call add_json_bool, Release_expose_flagged_api,        $(RELEASE_EXPOSE_FLAGGED_API))
 
 $(call add_json_str,  Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))
 
@@ -142,7 +144,6 @@
 $(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
 $(call add_json_str,  DeviceVndkVersion,                 $(BOARD_VNDK_VERSION))
 $(call add_json_str,  Platform_vndk_version,             $(PLATFORM_VNDK_VERSION))
-$(call add_json_str,  ProductVndkVersion,                $(PRODUCT_PRODUCT_VNDK_VERSION))
 $(call add_json_list, ExtraVndkVersions,                 $(PRODUCT_EXTRA_VNDK_VERSIONS))
 $(call add_json_list, DeviceSystemSdkVersions,           $(BOARD_SYSTEMSDK_VERSIONS))
 $(call add_json_str,  RecoverySnapshotVersion,           $(RECOVERY_SNAPSHOT_VERSION))
diff --git a/core/tasks/general-tests.mk b/core/tasks/general-tests.mk
index 8dbc76f..fb2a6be 100644
--- a/core/tasks/general-tests.mk
+++ b/core/tasks/general-tests.mk
@@ -44,22 +44,14 @@
 include $(BUILD_SYSTEM)/tasks/tools/vts-kernel-tests.mk
 ltp_copy_pairs := \
   $(call target-native-copy-pairs,$(kernel_ltp_modules),$(kernel_ltp_host_out))
-kselftest_copy_pairs := \
-  $(call target-native-copy-pairs,$(kernel_kselftest_modules),$(kernel_kselftest_host_out))
 copy_ltp_tests := $(call copy-many-files,$(ltp_copy_pairs))
-copy_kselftest_tests := $(call copy-many-files,$(kselftest_copy_pairs))
 
-# PHONY target to be used to build and test `vts_ltp_tests` and `vts_kselftest_tests` without building full vts
+# PHONY target to be used to build and test `vts_ltp_tests` without building full vts
 .PHONY: vts_kernel_ltp_tests
 vts_kernel_ltp_tests: $(copy_ltp_tests)
 
-.PHONY: vts_kernel_kselftest_tests
-vts_kernel_kselftest_tests: $(copy_kselftest_tests)
-
 $(general_tests_zip) : $(copy_ltp_tests)
-$(general_tests_zip) : $(copy_kselftest_tests)
 $(general_tests_zip) : PRIVATE_KERNEL_LTP_HOST_OUT := $(kernel_ltp_host_out)
-$(general_tests_zip) : PRIVATE_KERNEL_KSELFTEST_HOST_OUT := $(kernel_kselftest_host_out)
 $(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip)
 $(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip) $(general_tests_host_shared_libs_zip)
 $(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools)
@@ -73,7 +65,6 @@
 	mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools
 	echo $(sort $(COMPATIBILITY.general-tests.FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list
 	find $(PRIVATE_KERNEL_LTP_HOST_OUT) >> $(PRIVATE_INTERMEDIATES_DIR)/list
-	find $(PRIVATE_KERNEL_KSELFTEST_HOST_OUT) >> $(PRIVATE_INTERMEDIATES_DIR)/list
 	grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true
 	grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true
 	grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true
diff --git a/core/tasks/tools/vts-kernel-tests.mk b/core/tasks/tools/vts-kernel-tests.mk
index bd115c9..e727dc1 100644
--- a/core/tasks/tools/vts-kernel-tests.mk
+++ b/core/tasks/tools/vts-kernel-tests.mk
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
--include external/linux-kselftest/android/kselftest_test_list.mk
 -include external/ltp/android/ltp_package_list.mk
 
 include $(BUILD_SYSTEM)/tasks/tools/vts_package_utils.mk
@@ -23,7 +22,3 @@
 kernel_ltp_modules := \
     ltp \
     $(ltp_packages)
-
-kernel_kselftest_host_out := $(HOST_OUT_TESTCASES)/vts_kernel_kselftest_tests
-kernel_kselftest_vts_out := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name)/testcases/vts_kernel_kselftest_tests
-kernel_kselftest_modules := $(kselftest_modules)
diff --git a/core/tasks/vts-core-tests.mk b/core/tasks/vts-core-tests.mk
index bd7652b..1eeb078 100644
--- a/core/tasks/vts-core-tests.mk
+++ b/core/tasks/vts-core-tests.mk
@@ -20,13 +20,10 @@
 
 ltp_copy_pairs := \
   $(call target-native-copy-pairs,$(kernel_ltp_modules),$(kernel_ltp_vts_out))
-kselftest_copy_pairs := \
-  $(call target-native-copy-pairs,$(kernel_kselftest_modules),$(kernel_kselftest_vts_out))
 
 copy_ltp_tests := $(call copy-many-files,$(ltp_copy_pairs))
-copy_kselftest_tests := $(call copy-many-files,$(kselftest_copy_pairs))
 
-test_suite_extra_deps := $(copy_ltp_tests) $(copy_kselftest_tests)
+test_suite_extra_deps := $(copy_ltp_tests)
 
 include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
 
diff --git a/core/version_util.mk b/core/version_util.mk
index dca7482..f08b2f9 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -79,12 +79,23 @@
 # Build a list of all active code names. Avoid duplicates, and stop when we
 # reach a codename that matches PLATFORM_VERSION_CODENAME (anything beyond
 # that is not included in our build).
+#
+# REL is filtered out of the list. The codename of the current release is
+# replaced by "REL" when the build is configured as a release rather than a
+# preview. For example, PLATFORM_VERSION_CODENAME.UpsideDownCake will be "REL"
+# rather than UpsideDownCake in a -next target when the upcoming release is
+# UpsideDownCake. "REL" shouldn't really be treated as a codename though. It's a
+# placeholder to indicate that the build is a release and so doesn't really have
+# a codename. The list of all codenames ends up in
+# ro.build.version.all_codenames, and also ends up feeding the logic for stub
+# generation in soong, neither of which are places that should include REL.
 _versions_in_target := \
   $(call find_and_earlier,$(ALL_VERSIONS),$(TARGET_PLATFORM_VERSION))
 $(foreach version,$(_versions_in_target),\
   $(eval _codename := $(PLATFORM_VERSION_CODENAME.$(version)))\
-  $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
-    $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename))))
+  $(if $(filter REL,$(_codename)),,\
+      $(if $(filter $(_codename),$(PLATFORM_VERSION_ALL_CODENAMES)),,\
+        $(eval PLATFORM_VERSION_ALL_CODENAMES += $(_codename)))))
 
 # And the list of actually all the codenames that are in preview. The
 # ALL_CODENAMES variable is sort of a lie for historical reasons and only
diff --git a/target/product/gsi/Android.bp b/target/product/gsi/Android.bp
index a8af9c4..45ba143 100644
--- a/target/product/gsi/Android.bp
+++ b/target/product/gsi/Android.bp
@@ -23,3 +23,26 @@
         "*.txt",
     ],
 }
+
+prebuilt_etc {
+    name: "gsi_skip_mount.cfg",
+    filename: "skip_mount.cfg",
+    src: "gsi_skip_mount.cfg",
+
+    system_ext_specific: true,
+    relative_install_path: "init/config",
+
+    required: ["gsi_skip_mount_compat_symlink"],
+}
+
+// Adds a symlink under /system/etc/init/config pointing to /system/system_ext/etc/init/config
+// because first-stage init in Android 10.0 will read the skip_mount.cfg from /system/etc/* after
+// chroot /system.
+// TODO: remove this symlink when no need to support new GSI on Android 10.
+// The actual file needs to be under /system/system_ext because it's GSI-specific and does not
+// belong to core CSI.
+install_symlink {
+    name: "gsi_skip_mount_compat_symlink",
+    installed_location: "etc/init/config",
+    symlink_target: "/system/system_ext/etc/init/config",
+}
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 563712a..70ee78d 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -240,32 +240,6 @@
 include $(BUILD_PHONY_PACKAGE)
 
 #####################################################################
-# skip_mount.cfg, read by init to skip mounting some partitions when GSI is used.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := gsi_skip_mount.cfg
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_STEM := skip_mount.cfg
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := init/config
-
-# Adds a symlink under /system/etc/init/config pointing to /system/system_ext/etc/init/config
-# because first-stage init in Android 10.0 will read the skip_mount.cfg from /system/etc/* after
-# chroot /system.
-# TODO: remove this symlink when no need to support new GSI on Android 10.
-# The actual file needs to be under /system/system_ext because it's GSI-specific and does not
-# belong to core CSI.
-LOCAL_POST_INSTALL_CMD := \
-    mkdir -p $(TARGET_OUT)/etc/init; \
-    ln -sf /system/system_ext/etc/init/config $(TARGET_OUT)/etc/init/config
-
-include $(BUILD_PREBUILT)
-
-#####################################################################
 # init.gsi.rc, GSI-specific init script.
 
 include $(CLEAR_VARS)
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index bd85b9f..23eb534 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -28,9 +28,8 @@
 
 BUILDING_GSI := true
 
-# Exclude all files under system/product and system/system_ext,
-# and the vndk apex's compat symlinks
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
+    system/etc/init/config \
     system/product/% \
     system/system_ext/% \
     system/lib/vndk-29 \
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 702ef22..c4fc405 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -214,12 +214,12 @@
             return value;
         }
         private Map<String, Boolean> mFlagMap = new HashMap<>(
-            Map.of(
-                Flags.FLAG_DISABLED_RO, false,
-                Flags.FLAG_DISABLED_RW, false,
-                Flags.FLAG_ENABLED_FIXED_RO, false,
-                Flags.FLAG_ENABLED_RO, false,
-                Flags.FLAG_ENABLED_RW, false
+            Map.ofEntries(
+                Map.entry(Flags.FLAG_DISABLED_RO, false),
+                Map.entry(Flags.FLAG_DISABLED_RW, false),
+                Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
+                Map.entry(Flags.FLAG_ENABLED_RO, false),
+                Map.entry(Flags.FLAG_ENABLED_RW, false)
             )
         );
     }
diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
index e4a1fb7..d2cea95 100644
--- a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -37,9 +37,10 @@
     }
 
     private Map<String, Boolean> mFlagMap = new HashMap<>(
-        Map.of(
+        Map.ofEntries(
             {{-for item in class_elements}}
-            Flags.FLAG_{item.flag_name_constant_suffix}, false{{ if not @last }},{{ endif }}
+            Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false)
+            {{ -if not @last }},{{ endif }}
             {{ -endfor }}
         )
     );
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 31f8736..fc4ab68 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -42,6 +42,10 @@
   --is_signing
       Skip building & adding the images for "userdata" and "cache" if we
       are signing the target files.
+
+  --avb-resolve-rollback-index-location-conflict
+      If provided, resolve the conflict AVB rollback index location when
+      necessary.
 """
 
 from __future__ import print_function
@@ -81,6 +85,7 @@
 OPTIONS.rebuild_recovery = False
 OPTIONS.replace_updated_files_list = []
 OPTIONS.is_signing = False
+OPTIONS.avb_resolve_rollback_index_location_conflict = False
 
 
 def ParseAvbFooter(img_path) -> avbtool.AvbFooter:
@@ -682,7 +687,8 @@
     logger.info("%s.img already exists; not rebuilding...", name)
     return img.name
 
-  common.BuildVBMeta(img.name, partitions, name, needed_partitions)
+  common.BuildVBMeta(img.name, partitions, name, needed_partitions,
+                     OPTIONS.avb_resolve_rollback_index_location_conflict)
   img.Write()
   return img.name
 
@@ -1224,6 +1230,8 @@
                        " please switch to AVB")
     elif o == "--is_signing":
       OPTIONS.is_signing = True
+    elif o == "--avb_resolve_rollback_index_location_conflict":
+      OPTIONS.avb_resolve_rollback_index_location_conflict = True
     else:
       return False
     return True
@@ -1233,7 +1241,8 @@
       extra_long_opts=["add_missing", "rebuild_recovery",
                        "replace_verity_public_key=",
                        "replace_verity_private_key=",
-                       "is_signing"],
+                       "is_signing",
+                       "avb_resolve_rollback_index_location_conflict"],
       extra_option_handler=option_handler)
 
   if len(args) != 1:
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a3daca5..0f3c430 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -20,7 +20,6 @@
 import datetime
 import errno
 import fnmatch
-from genericpath import isdir
 import getopt
 import getpass
 import gzip
@@ -34,12 +33,14 @@
 import shlex
 import shutil
 import subprocess
-import sys
 import stat
+import sys
 import tempfile
 import threading
 import time
 import zipfile
+from dataclasses import dataclass
+from genericpath import isdir
 from hashlib import sha1, sha256
 
 import images
@@ -112,13 +113,18 @@
 # descriptor into vbmeta.img. When adding a new entry here, the
 # AVB_FOOTER_ARGS_BY_PARTITION in sign_target_files_apks need to be updated
 # accordingly.
-AVB_PARTITIONS = ('boot', 'init_boot', 'dtbo', 'odm', 'product', 'pvmfw', 'recovery',
-                  'system', 'system_ext', 'vendor', 'vendor_boot', 'vendor_kernel_boot',
-                  'vendor_dlkm', 'odm_dlkm', 'system_dlkm')
+AVB_PARTITIONS = ('boot', 'init_boot', 'dtbo', 'odm', 'product', 'pvmfw',
+                  'recovery', 'system', 'system_ext', 'vendor', 'vendor_boot',
+                  'vendor_kernel_boot', 'vendor_dlkm', 'odm_dlkm',
+                  'system_dlkm')
 
 # Chained VBMeta partitions.
 AVB_VBMETA_PARTITIONS = ('vbmeta_system', 'vbmeta_vendor')
 
+# avbtool arguments name
+AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG = '--include_descriptors_from_image'
+AVB_ARG_NAME_CHAIN_PARTITION = '--chain_partition'
+
 # Partitions that should have their care_map added to META/care_map.pb
 PARTITIONS_WITH_CARE_MAP = [
     'system',
@@ -139,6 +145,19 @@
 RAMDISK_BUILD_PROP_REL_PATHS = ['system/etc/ramdisk/build.prop']
 
 
+@dataclass
+class AvbChainedPartitionArg:
+  """The required arguments for avbtool --chain_partition."""
+  partition: str
+  rollback_index_location: int
+  pubkey_path: str
+
+  def to_string(self):
+    """Convert to string command arguments."""
+    return '{}:{}:{}'.format(
+        self.partition, self.rollback_index_location, self.pubkey_path)
+
+
 class ErrorCode(object):
   """Define error_codes for failures that happen during the actual
   update package installation.
@@ -1405,7 +1424,7 @@
   return RunAndCheckOutput(cmd)
 
 
-def AppendAVBSigningArgs(cmd, partition):
+def AppendAVBSigningArgs(cmd, partition, avb_salt=None):
   """Append signing arguments for avbtool."""
   # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
   key_path = ResolveAVBSigningPathArgs(
@@ -1413,7 +1432,8 @@
   algorithm = OPTIONS.info_dict.get("avb_" + partition + "_algorithm")
   if key_path and algorithm:
     cmd.extend(["--key", key_path, "--algorithm", algorithm])
-  avb_salt = OPTIONS.info_dict.get("avb_salt")
+  if avb_salt is None:
+    avb_salt = OPTIONS.info_dict.get("avb_salt")
   # make_vbmeta_image doesn't like "--salt" (and it's not needed).
   if avb_salt and not partition.startswith("vbmeta"):
     cmd.extend(["--salt", avb_salt])
@@ -1447,7 +1467,7 @@
 
 
 def GetAvbPartitionArg(partition, image, info_dict=None):
-  """Returns the VBMeta arguments for partition.
+  """Returns the VBMeta arguments for one 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.
@@ -1459,7 +1479,7 @@
         OPTIONS.info_dict if None has been given.
 
   Returns:
-    A list of VBMeta arguments.
+    A list of VBMeta arguments for one partition.
   """
   if info_dict is None:
     info_dict = OPTIONS.info_dict
@@ -1467,7 +1487,7 @@
   # Check if chain partition is used.
   key_path = info_dict.get("avb_" + partition + "_key_path")
   if not key_path:
-    return ["--include_descriptors_from_image", image]
+    return [AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image]
 
   # For a non-A/B device, we don't chain /recovery nor include its descriptor
   # into vbmeta.img. The recovery image will be configured on an independent
@@ -1479,7 +1499,62 @@
 
   # Otherwise chain the partition into vbmeta.
   chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)
-  return ["--chain_partition", chained_partition_arg]
+  return [AVB_ARG_NAME_CHAIN_PARTITION, chained_partition_arg]
+
+
+def GetAvbPartitionsArg(partitions,
+                        resolve_rollback_index_location_conflict=False,
+                        info_dict=None):
+  """Returns the VBMeta arguments for all AVB partitions.
+
+  It sets up the VBMeta argument by calling GetAvbPartitionArg of all
+  partitions.
+
+  Args:
+    partitions: A dict of all AVB partitions.
+    resolve_rollback_index_location_conflict: If true, resolve conflicting avb
+        rollback index locations by assigning the smallest unused value.
+    info_dict: A dict returned by common.LoadInfoDict().
+
+  Returns:
+    A list of VBMeta arguments for all partitions.
+  """
+  # An AVB partition will be linked into a vbmeta partition by either
+  # AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG or AVB_ARG_NAME_CHAIN_PARTITION, there
+  # should be no other cases.
+  valid_args = {
+      AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG: [],
+      AVB_ARG_NAME_CHAIN_PARTITION: []
+  }
+
+  for partition, path in partitions.items():
+    avb_partition_arg = GetAvbPartitionArg(partition, path, info_dict)
+    if not avb_partition_arg:
+      continue
+    arg_name, arg_value = avb_partition_arg
+    assert arg_name in valid_args
+    valid_args[arg_name].append(arg_value)
+
+  # Copy the arguments for non-chained AVB partitions directly without
+  # intervention.
+  avb_args = []
+  for image in valid_args[AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG]:
+    avb_args.extend([AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image])
+
+  # Handle chained AVB partitions. The rollback index location might be
+  # adjusted if two partitions use the same value. This may happen when mixing
+  # a shared system image with other vendor images.
+  used_index_loc = set()
+  for chained_partition_arg in valid_args[AVB_ARG_NAME_CHAIN_PARTITION]:
+    if resolve_rollback_index_location_conflict:
+      while chained_partition_arg.rollback_index_location in used_index_loc:
+        chained_partition_arg.rollback_index_location += 1
+
+    used_index_loc.add(chained_partition_arg.rollback_index_location)
+    avb_args.extend([AVB_ARG_NAME_CHAIN_PARTITION,
+                     chained_partition_arg.to_string()])
+
+  return avb_args
 
 
 def GetAvbChainedPartitionArg(partition, info_dict, key=None):
@@ -1493,8 +1568,8 @@
         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.
+    An AvbChainedPartitionArg object with rollback_index_location and
+    pubkey_path that can be used to build or verify vbmeta image.
   """
   if key is None:
     key = info_dict["avb_" + partition + "_key_path"]
@@ -1502,7 +1577,10 @@
   pubkey_path = ExtractAvbPublicKey(info_dict["avb_avbtool"], key)
   rollback_index_location = info_dict[
       "avb_" + partition + "_rollback_index_location"]
-  return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
+  return AvbChainedPartitionArg(
+      partition=partition,
+      rollback_index_location=int(rollback_index_location),
+      pubkey_path=pubkey_path)
 
 
 def _HasGkiCertificationArgs():
@@ -1549,7 +1627,8 @@
   return data
 
 
-def BuildVBMeta(image_path, partitions, name, needed_partitions):
+def BuildVBMeta(image_path, partitions, name, needed_partitions,
+                resolve_rollback_index_location_conflict=False):
   """Creates a VBMeta image.
 
   It generates the requested VBMeta image. The requested image could be for
@@ -1564,6 +1643,8 @@
     name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
     needed_partitions: Partitions whose descriptors should be included into the
         generated VBMeta image.
+    resolve_rollback_index_location_conflict: If true, resolve conflicting avb
+        rollback index locations by assigning the smallest unused value.
 
   Raises:
     AssertionError: On invalid input args.
@@ -1577,6 +1658,7 @@
   custom_avb_partitions = ["vbmeta_" + part for part in OPTIONS.info_dict.get(
       "avb_custom_vbmeta_images_partition_list", "").strip().split()]
 
+  avb_partitions = {}
   for partition, path in partitions.items():
     if partition not in needed_partitions:
       continue
@@ -1587,7 +1669,9 @@
         'Unknown partition: {}'.format(partition)
     assert os.path.exists(path), \
         'Failed to find {} for {}'.format(path, partition)
-    cmd.extend(GetAvbPartitionArg(partition, path))
+    avb_partitions[partition] = path
+  cmd.extend(GetAvbPartitionsArg(avb_partitions,
+                                 resolve_rollback_index_location_conflict))
 
   args = OPTIONS.info_dict.get("avb_{}_args".format(name))
   if args and args.strip():
@@ -1598,7 +1682,7 @@
       # same location when running this script (we have the input target_files
       # zip only). For such cases, we additionally scan other locations (e.g.
       # IMAGES/, RADIO/, etc) before bailing out.
-      if arg == '--include_descriptors_from_image':
+      if arg == AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG:
         chained_image = split_args[index + 1]
         if os.path.exists(chained_image):
           continue
@@ -1820,7 +1904,11 @@
     cmd = [avbtool, "add_hash_footer", "--image", img.name,
            "--partition_size", str(part_size), "--partition_name",
            partition_name]
-    AppendAVBSigningArgs(cmd, partition_name)
+    salt = None
+    if kernel_path is not None:
+      with open(kernel_path, "rb") as fp:
+        salt = sha256(fp.read()).hexdigest()
+    AppendAVBSigningArgs(cmd, partition_name, salt)
     args = info_dict.get("avb_" + partition_name + "_add_hash_footer_args")
     if args and args.strip():
       split_args = ResolveAVBSigningPathArgs(shlex.split(args))
diff --git a/tools/releasetools/merge/merge_target_files.py b/tools/releasetools/merge/merge_target_files.py
index a0d3a1c..6bf1b49 100755
--- a/tools/releasetools/merge/merge_target_files.py
+++ b/tools/releasetools/merge/merge_target_files.py
@@ -90,6 +90,10 @@
   --keep-tmp
       Keep tempoary files for debugging purposes.
 
+  --avb-resolve-rollback-index-location-conflict
+      If provided, resolve the conflict AVB rollback index location when
+      necessary.
+
   The following only apply when using the VSDK to perform dexopt on vendor apps:
 
   --framework-dexpreopt-config
@@ -144,6 +148,7 @@
 OPTIONS.vendor_otatools = None
 OPTIONS.rebuild_sepolicy = False
 OPTIONS.keep_tmp = False
+OPTIONS.avb_resolve_rollback_index_location_conflict = False
 OPTIONS.framework_dexpreopt_config = None
 OPTIONS.framework_dexpreopt_tools = None
 OPTIONS.vendor_dexpreopt_config = None
@@ -230,6 +235,8 @@
   ]
   if OPTIONS.rebuild_recovery:
     add_img_args.append('--rebuild_recovery')
+  if OPTIONS.avb_resolve_rollback_index_location_conflict:
+    add_img_args.append('--avb_resolve_rollback_index_location_conflict')
   add_img_args.append(target_files_dir)
 
   add_img_to_target_files.main(add_img_args)
@@ -554,6 +561,8 @@
       OPTIONS.rebuild_sepolicy = True
     elif o == '--keep-tmp':
       OPTIONS.keep_tmp = True
+    elif o == '--avb-resolve-rollback-index-location-conflict':
+      OPTIONS.avb_resolve_rollback_index_location_conflict = True
     elif o == '--framework-dexpreopt-config':
       OPTIONS.framework_dexpreopt_config = a
     elif o == '--framework-dexpreopt-tools':
@@ -593,6 +602,7 @@
           'vendor-otatools=',
           'rebuild-sepolicy',
           'keep-tmp',
+          'avb-resolve-rollback-index-location-conflict',
       ],
       extra_option_handler=option_handler)
 
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index c69a13d..14f0e88 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1299,11 +1299,11 @@
         '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]))
+    chained_partition_args = common.GetAvbChainedPartitionArg(
+        'system', info_dict)
+    self.assertEqual('system', chained_partition_args.partition)
+    self.assertEqual(2, chained_partition_args.rollback_index_location)
+    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_withPrivateKey(self):
@@ -1313,11 +1313,11 @@
         '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]))
+    chained_partition_args = common.GetAvbChainedPartitionArg(
+        'product', info_dict)
+    self.assertEqual('product', chained_partition_args.partition)
+    self.assertEqual(2, chained_partition_args.rollback_index_location)
+    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
@@ -1327,12 +1327,11 @@
         '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]))
+    chained_partition_args = common.GetAvbChainedPartitionArg(
+        'system', info_dict, pubkey)
+    self.assertEqual('system', chained_partition_args.partition)
+    self.assertEqual(2, chained_partition_args.rollback_index_location)
+    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_GetAvbChainedPartitionArg_invalidKey(self):
@@ -1585,7 +1584,8 @@
     info_dict = {}
     cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
     self.assertEqual(
-        ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
+        [common.AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, '/path/to/system.img'],
+        cmd)
 
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
@@ -1598,12 +1598,11 @@
     }
     cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
     self.assertEqual(2, len(cmd))
-    self.assertEqual('--chain_partition', cmd[0])
-    chained_partition_args = cmd[1].split(':')
-    self.assertEqual(3, len(chained_partition_args))
-    self.assertEqual('vendor', chained_partition_args[0])
-    self.assertEqual('5', chained_partition_args[1])
-    self.assertTrue(os.path.exists(chained_partition_args[2]))
+    self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
+    chained_partition_args = cmd[1]
+    self.assertEqual('vendor', chained_partition_args.partition)
+    self.assertEqual(5, chained_partition_args.rollback_index_location)
+    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
   @test_utils.SkipIfExternalToolsUnavailable()
   def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
@@ -1631,12 +1630,11 @@
     cmd = common.GetAvbPartitionArg(
         'recovery', '/path/to/recovery.img', info_dict)
     self.assertEqual(2, len(cmd))
-    self.assertEqual('--chain_partition', cmd[0])
-    chained_partition_args = cmd[1].split(':')
-    self.assertEqual(3, len(chained_partition_args))
-    self.assertEqual('recovery', chained_partition_args[0])
-    self.assertEqual('3', chained_partition_args[1])
-    self.assertTrue(os.path.exists(chained_partition_args[2]))
+    self.assertEqual(common.AVB_ARG_NAME_CHAIN_PARTITION, cmd[0])
+    chained_partition_args = cmd[1]
+    self.assertEqual('recovery', chained_partition_args.partition)
+    self.assertEqual(3, chained_partition_args.rollback_index_location)
+    self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
   def test_GenerateGkiCertificate_KeyPathNotFound(self):
     pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index beb9e75..82b3107 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -430,7 +430,8 @@
         key_file = options.get(key_name, info_dict[key_name])
         chained_partition_arg = common.GetAvbChainedPartitionArg(
             partition, info_dict, key_file)
-        cmd.extend(['--expected_chain_partition', chained_partition_arg])
+        cmd.extend(['--expected_chain_partition',
+                    chained_partition_arg.to_string()])
 
     # Handle the boot image with a non-default name, e.g. boot-5.4.img
     boot_images = info_dict.get("boot_images")