Merge "Remove TrimmedApex product variable" into main
diff --git a/ci/test_discovery_agent.py b/ci/test_discovery_agent.py
index 89d35d7..4eed28d 100644
--- a/ci/test_discovery_agent.py
+++ b/ci/test_discovery_agent.py
@@ -11,18 +11,33 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+"""Test discovery agent that uses TradeFed to discover test artifacts."""
+import glob
+import json
+import logging
+import os
+import subprocess
+import buildbot
+
+
 class TestDiscoveryAgent:
   """Test discovery agent."""
 
-  _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
-      "tools/tradefederation/prebuilts/filegroups/tradefed/"
+  _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
+      "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/"
   )
 
+  _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery"
+
+  _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes"
+
+  _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt"
+
   def __init__(
       self,
       tradefed_args: list[str],
-      test_mapping_zip_path: str,
-      tradefed_jar_revelant_files_path: str = _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
+      test_mapping_zip_path: str = "",
+      tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
   ):
     self.tradefed_args = tradefed_args
     self.test_mapping_zip_path = test_mapping_zip_path
@@ -34,7 +49,46 @@
     Returns:
       A list of test zip regexes that TF is going to try to pull files from.
     """
-    return []
+    test_discovery_output_file_name = os.path.join(
+        buildbot.OutDir(), self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
+    )
+    with open(
+        test_discovery_output_file_name, mode="w+t"
+    ) as test_discovery_output_file:
+      java_args = []
+      java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java")
+      java_args.append("-cp")
+      java_args.append(
+          self.create_classpath(self.tradefed_jar_relevant_files_path)
+      )
+      java_args.append(
+          "com.android.tradefed.observatory.TestZipDiscoveryExecutor"
+      )
+      java_args.extend(self.tradefed_args)
+      env = os.environ.copy()
+      env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name})
+      logging.info(f"Calling test discovery with args: {java_args}")
+      try:
+        result = subprocess.run(args=java_args, env=env, text=True, check=True)
+        logging.info(f"Test zip discovery output: {result.stdout}")
+      except subprocess.CalledProcessError as e:
+        raise TestDiscoveryError(
+            f"Failed to run test discovery, strout: {e.stdout}, strerr:"
+            f" {e.stderr}, returncode: {e.returncode}"
+        )
+      data = json.loads(test_discovery_output_file.read())
+      logging.info(f"Test discovery result file content: {data}")
+      if (
+          self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data
+          and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY]
+      ):
+        raise TestDiscoveryError("No possible test discovery")
+      if (
+          data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None
+          or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is []
+      ):
+        raise TestDiscoveryError("No test zip regexes returned")
+      return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY]
 
   def discover_test_modules(self) -> list[str]:
     """Discover test modules from TradeFed.
@@ -44,3 +98,24 @@
       TradeFed test args.
     """
     return []
+
+  def create_classpath(self, directory):
+    """Creates a classpath string from all .jar files in the given directory.
+
+    Args:
+      directory: The directory to search for .jar files.
+
+    Returns:
+      A string representing the classpath, with jar files separated by the
+      OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows).
+    """
+    jar_files = glob.glob(os.path.join(directory, "*.jar"))
+    return os.pathsep.join(jar_files)
+
+
+class TestDiscoveryError(Exception):
+  """A TestDiscoveryErrorclass."""
+
+  def __init__(self, message):
+    super().__init__(message)
+    self.message = message
diff --git a/core/Makefile b/core/Makefile
index 81ae6f7..43618e3 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -692,7 +692,7 @@
 
 BOARD_KERNEL_MODULE_DIRS += top
 
-# Default to not generating modules.dep for kernel modules on system
+# Default to not generating modules.load for kernel modules on system
 # side. We should only load these modules if they are depended by vendor
 # side modules.
 ifeq ($(BOARD_SYSTEM_KERNEL_MODULES_LOAD),)
@@ -1676,12 +1676,13 @@
   INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_cmdline "$(INTERNAL_KERNEL_CMDLINE)"
 endif
 
-ifdef INTERNAL_BOOTCONFIG
+ifneq (, $(INTERNAL_BOOTCONFIG)$(INTERNAL_BOOTCONFIG_FILE))
   INTERNAL_VENDOR_BOOTCONFIG_TARGET := $(PRODUCT_OUT)/vendor-bootconfig.img
   $(INTERNAL_VENDOR_BOOTCONFIG_TARGET):
 	rm -f $@
 	$(foreach param,$(INTERNAL_BOOTCONFIG), \
 	 printf "%s\n" $(param) >> $@;)
+	cat $(INTERNAL_BOOTCONFIG_FILE) >> $@
   INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_bootconfig $(INTERNAL_VENDOR_BOOTCONFIG_TARGET)
 endif
 
@@ -3488,12 +3489,14 @@
 #   $(2): The partition's staging directory
 #   $(3): Files to include in the partition
 define write-partition-file-list
+$(1): PRIVATE_FILES := $(subst $(2)/,,$(filter $(2)/%,$(3)))
+$(1): PRIVATE_EXTRA_INSTALL_ZIPS := $(call relevant-extra-install-zips,$(filter $(2)/%,$(3)))
 $(1): $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(foreach p,$(call relevant-extra-install-zips,$(filter $(2)/%,$(3))),$(call word-colon,3,$(p)))
 	@echo Writing $$@
 	rm -f $$@
 	echo -n > $$@
-	$$(foreach f,$(subst $(2)/,,$(filter $(2)/%,$(3))),echo "$$(f)" >> $$@$$(newline))
-	$$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $(call relevant-extra-install-zips,$(filter $(2)/%,$(3))) >> $$@
+	$$(foreach f,$$(PRIVATE_FILES),echo "$$(f)" >> $$@$$(newline))
+	$$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $$(PRIVATE_EXTRA_INSTALL_ZIPS) >> $$@
 endef
 
 # -----------------------------------------------------------------
@@ -5295,7 +5298,7 @@
 	  --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \
 	  --dirmap /product:$(TARGET_OUT_PRODUCT) \
 	  --dirmap /apex:$(APEX_OUT) \
-	  $(VINTF_FRAMEWORK_MANIFEST_FROZEN_DIR) > $@ 2>&1 ) || ( cat $@ && exit 1 )
+	  system/libhidl/vintfdata/frozen > $@ 2>&1 ) || ( cat $@ && exit 1 )
 
 $(call declare-1p-target,$(vintffm_log))
 
@@ -6380,6 +6383,10 @@
   endif
 endif
 
+ifdef BUILDING_VENDOR_KERNEL_BOOT_IMAGE
+  $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES)
+endif
+
 ifdef BUILDING_RECOVERY_IMAGE
   # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other
   # BUILT_TARGET_FILES_PACKAGE dependencies because currently there're cp/rsync/rm
@@ -7973,7 +7980,7 @@
 
 # -----------------------------------------------------------------
 # Desktop pack recovery image hook.
-ifneq (,$(strip $(PACK_DESKTOP_RECOVERY_IMAGE)))
+ifeq ($(BOARD_USES_DESKTOP_RECOVERY_IMAGE),true)
 PACK_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_recovery_image.bin
 PACK_RECOVERY_IMAGE_ARGS := --noarchive --recovery
 
@@ -7994,11 +8001,11 @@
 .PHONY: pack-recovery-image
 pack-recovery-image: $(PACK_RECOVERY_IMAGE_TARGET)
 
-endif # PACK_DESKTOP_RECOVERY_IMAGE
+endif # BOARD_USES_DESKTOP_RECOVERY_IMAGE
 
 # -----------------------------------------------------------------
 # Desktop pack update image hook.
-ifneq (,$(strip $(PACK_DESKTOP_UPDATE_IMAGE)))
+ifeq ($(BOARD_USES_DESKTOP_UPDATE_IMAGE),true)
 PACK_UPDATE_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_update_image.bin
 PACK_UPDATE_IMAGE_ARGS := --noarchive --update
 
@@ -8019,7 +8026,7 @@
 .PHONY: pack-update-image
 pack-update-image: $(PACK_UPDATE_IMAGE_TARGET)
 
-endif # PACK_DESKTOP_UPDATE_IMAGE
+endif # BOARD_USES_DESKTOP_UPDATE_IMAGE
 
 PACK_MIGRATION_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_migration_image
 
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index e5a8e44..26fe1da 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -42,6 +42,12 @@
 
 $(call add_soong_config_var,ANDROID,ADDITIONAL_M4DEFS,$(if $(BOARD_SEPOLICY_M4DEFS),$(addprefix -D,$(BOARD_SEPOLICY_M4DEFS))))
 
+# For bootable/recovery
+RECOVERY_API_VERSION := 3
+RECOVERY_FSTAB_VERSION := 2
+$(call soong_config_set, recovery, recovery_api_version, $(RECOVERY_API_VERSION))
+$(call soong_config_set, recovery, recovery_fstab_version, $(RECOVERY_FSTAB_VERSION))
+
 # For Sanitizers
 $(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false))
 $(call soong_config_set_bool,ANDROID,HWASAN_ENABLED,$(if $(filter hwaddress,$(SANITIZE_TARGET)),true,false))
@@ -65,6 +71,11 @@
 # The default value of ART_BUILD_HOST_DEBUG is true
 $(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true))
 
+# For chre
+$(call soong_config_set_bool,chre,chre_daemon_lama_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false))
+
 ifdef TARGET_BOARD_AUTO
   $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))
 endif
@@ -96,6 +107,10 @@
 $(call add_soong_config_var_value,ANDROID,avf_microdroid_guest_gki_version,$(PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION))
 endif
 
+ifdef TARGET_BOOTS_16K
+$(call soong_config_set_bool,ANDROID,target_boots_16k,$(filter true,$(TARGET_BOOTS_16K)))
+endif
+
 ifdef PRODUCT_MEMCG_V2_FORCE_ENABLED
 $(call add_soong_config_var_value,ANDROID,memcg_v2_force_enabled,$(PRODUCT_MEMCG_V2_FORCE_ENABLED))
 endif
@@ -219,5 +234,45 @@
 $(call soong_config_set_bool,video_codec,board_use_codec2_hidl_1_2,$(if $(filter true,$(BOARD_USE_CODEC2_HIDL_1_2)),true,false))
 $(call soong_config_set_bool,video_codec,board_support_mfc_enc_bt2020,$(if $(filter true,$(BOARD_SUPPORT_MFC_ENC_BT2020)),true,false))
 $(call soong_config_set_bool,video_codec,board_support_flexible_p010,$(if $(filter true,$(BOARD_SUPPORT_FLEXIBLE_P010)),true,false))
-$(call soong_config_set,video_codec,board_support_mfc_version,$(BOARD_SUPPORT_MFC_VERSION))
 $(call soong_config_set_bool,video_codec,board_use_codec2_aidl,$(if $(BOARD_USE_CODEC2_AIDL),true,false))
+$(call soong_config_set,video_codec,board_gpu_type,$(BOARD_GPU_TYPE))
+$(call soong_config_set_bool,video_codec,board_use_small_secure_memory,$(if $(filter true,$(BOARD_USE_SMALL_SECURE_MEMORY)),true,false))
+ifdef BOARD_SUPPORT_MFC_VERSION
+  $(call soong_config_set,video_codec,board_support_mfc_version,$(BOARD_SUPPORT_MFC_VERSION))
+endif
+ifdef BOARD_USE_MAX_SECURE_RESOURCE
+  $(call soong_config_set,video_codec,board_use_max_secure_resource,$(BOARD_USE_MAX_SECURE_RESOURCE))
+endif
+
+# Export related variables to soong for hardware/google/graphics/common/libacryl:libacryl
+ifdef BOARD_LIBACRYL_DEFAULT_COMPOSITOR
+  $(call soong_config_set,acryl,libacryl_default_compositor,$(BOARD_LIBACRYL_DEFAULT_COMPOSITOR))
+endif
+ifdef BOARD_LIBACRYL_DEFAULT_SCALER
+  $(call soong_config_set,acryl,libacryl_default_scaler,$(BOARD_LIBACRYL_DEFAULT_SCALER))
+endif
+ifdef BOARD_LIBACRYL_DEFAULT_BLTER
+  $(call soong_config_set,acryl,libacryl_default_blter,$(BOARD_LIBACRYL_DEFAULT_BLTER))
+endif
+ifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN
+  #BOARD_LIBACRYL_G2D_HDR_PLUGIN is set in each board config
+  $(call soong_config_set_bool,acryl,libacryl_use_g2d_hdr_plugin,true)
+endif
+
+# Export related variables to soong for hardware/google/graphics/common/BoardConfigCFlags.mk
+$(call soong_config_set_bool,google_graphics,hwc_no_support_skip_validate,$(if $(filter true,$(HWC_NO_SUPPORT_SKIP_VALIDATE)),true,false))
+$(call soong_config_set_bool,google_graphics,hwc_support_color_transform,$(if $(filter true,$(HWC_SUPPORT_COLOR_TRANSFORM)),true,false))
+$(call soong_config_set_bool,google_graphics,hwc_support_render_intent,$(if $(filter true,$(HWC_SUPPORT_RENDER_INTENT)),true,false))
+$(call soong_config_set_bool,google_graphics,board_uses_virtual_display,$(if $(filter true,$(BOARD_USES_VIRTUAL_DISPLAY)),true,false))
+$(call soong_config_set_bool,google_graphics,board_uses_dt,$(if $(filter true,$(BOARD_USES_DT)),true,false))
+$(call soong_config_set_bool,google_graphics,board_uses_decon_64bit_address,$(if $(filter true,$(BOARD_USES_DECON_64BIT_ADDRESS)),true,false))
+$(call soong_config_set_bool,google_graphics,board_uses_hdrui_gles_conversion,$(if $(filter true,$(BOARD_USES_HDRUI_GLES_CONVERSION)),true,false))
+$(call soong_config_set_bool,google_graphics,uses_idisplay_intf_sec,$(if $(filter true,$(USES_IDISPLAY_INTF_SEC)),true,false))
+
+# Variables for fs_config
+$(call soong_config_set_bool,fs_config,vendor,$(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),true,false))
+$(call soong_config_set_bool,fs_config,oem,$(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),true,false))
+$(call soong_config_set_bool,fs_config,odm,$(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),true,false))
+$(call soong_config_set_bool,fs_config,vendor_dlkm,$(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
+$(call soong_config_set_bool,fs_config,odm_dlkm,$(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
+$(call soong_config_set_bool,fs_config,system_dlkm,$(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false))
diff --git a/core/binary.mk b/core/binary.mk
index 3481144..ea862be 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -174,7 +174,7 @@
 endif
 endif
 
-my_ndk_sysroot_include :=
+my_ndk_sysroot :=
 my_ndk_sysroot_lib :=
 my_api_level := 10000
 
@@ -207,11 +207,9 @@
 
   my_built_ndk := $(SOONG_OUT_DIR)/ndk
   my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE)
-  my_ndk_sysroot_include := \
-      $(my_built_ndk)/sysroot/usr/include \
-      $(my_built_ndk)/sysroot/usr/include/$(my_ndk_triple) \
+  my_ndk_sysroot := $(my_built_ndk)/sysroot
 
-  my_ndk_sysroot_lib := $(my_built_ndk)/sysroot/usr/lib/$(my_ndk_triple)/$(my_ndk_api)
+  my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib/$(my_ndk_triple)/$(my_ndk_api)
 
   # The bionic linker now has support for packed relocations and gnu style
   # hashes (which are much faster!), but shipping to older devices requires
@@ -1628,19 +1626,6 @@
 ###########################################################
 ifndef LOCAL_IS_HOST_MODULE
 
-ifeq ($(call module-in-vendor-or-product),true)
-  my_target_global_c_includes :=
-  my_target_global_c_system_includes := $(TARGET_OUT_HEADERS)
-else ifdef LOCAL_SDK_VERSION
-  my_target_global_c_includes :=
-  my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
-else
-  my_target_global_c_includes := $(SRC_HEADERS) \
-    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
-  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
-    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
-endif
-
 my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)
 my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags)
 my_target_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags)
@@ -1656,6 +1641,22 @@
   my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS)
 endif # my_use_clang_lld
 
+ifeq ($(call module-in-vendor-or-product),true)
+  my_target_global_c_includes :=
+  my_target_global_c_system_includes := $(TARGET_OUT_HEADERS)
+  my_target_global_cflags += -nostdlibinc
+else ifdef LOCAL_SDK_VERSION
+  my_target_global_c_includes :=
+  my_target_global_c_system_includes := $(my_ndk_stl_include_path)
+  my_target_global_cflags += --sysroot $(my_ndk_sysroot)
+else
+  my_target_global_c_includes := $(SRC_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
+  my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
+  my_target_global_cflags += -nostdlibinc
+endif
+
 my_target_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)TRIPLE)
 ifndef LOCAL_IS_HOST_MODULE
   my_target_triple_flag := -target $(my_target_triple)$(my_api_level)
diff --git a/core/board_config.mk b/core/board_config.mk
index 5606964..ea0d022 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -27,6 +27,7 @@
 _board_strip_readonly_list += BOARD_KERNEL_CMDLINE
 _board_strip_readonly_list += BOARD_BOOT_HEADER_VERSION
 _board_strip_readonly_list += BOARD_BOOTCONFIG
+_board_strip_readonly_list += BOARD_BOOTCONFIG_FILE
 _board_strip_readonly_list += BOARD_KERNEL_BASE
 _board_strip_readonly_list += BOARD_USES_GENERIC_AUDIO
 _board_strip_readonly_list += BOARD_USES_RECOVERY_AS_BOOT
@@ -311,9 +312,10 @@
 .KATI_READONLY := $(_board_strip_readonly_list)
 
 INTERNAL_KERNEL_CMDLINE := $(BOARD_KERNEL_CMDLINE)
-ifneq (,$(BOARD_BOOTCONFIG))
+ifneq (,$(BOARD_BOOTCONFIG)$(BOARD_BOOTCONFIG_FILE))
   INTERNAL_KERNEL_CMDLINE += bootconfig
   INTERNAL_BOOTCONFIG := $(BOARD_BOOTCONFIG)
+  INTERNAL_BOOTCONFIG_FILE := $(BOARD_BOOTCONFIG_FILE)
 endif
 
 ifneq ($(filter %64,$(TARGET_ARCH)),)
@@ -922,6 +924,18 @@
 endif
 .KATI_READONLY := BOARD_USES_PVMFWIMAGE
 
+BOARD_USES_DESKTOP_RECOVERY_IMAGE :=
+ifeq ($(PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE),true)
+  BOARD_USES_DESKTOP_RECOVERY_IMAGE := true
+endif
+.KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_IMAGE
+
+BOARD_USES_DESKTOP_UPDATE_IMAGE :=
+ifeq ($(PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE),true)
+  BOARD_USES_DESKTOP_UPDATE_IMAGE := true
+endif
+.KATI_READONLY := BOARD_USES_DESKTOP_UPDATE_IMAGE
+
 ###########################################
 # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.
 TARGET_RECOVERY_UPDATER_LIBS ?=
diff --git a/core/config.mk b/core/config.mk
index 192c8b2..f9ba38c 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -432,13 +432,6 @@
 endif
 .KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED
 
-ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE
-  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE)
-else
-  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false
-endif
-.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE
-
 # Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.
 ifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO
   TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO)
@@ -817,6 +810,18 @@
   endif
 endif
 
+ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE
+  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE)
+else ifeq (true,$(TARGET_BUILD_UNBUNDLED))
+  # unbundled builds may not have updated build sources
+  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false
+else ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),)
+  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := true
+else
+  TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false
+endif
+.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE
+
 # Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching
 # devices if unset.
 ifndef BOARD_SYSTEMSDK_VERSIONS
@@ -839,12 +844,6 @@
 .KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES
 
 ifdef PRODUCT_SHIPPING_API_LEVEL
-  board_api_level := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL))
-  ifneq (,$(board_api_level))
-    min_systemsdk_version := $(call math_min,$(board_api_level),$(PRODUCT_SHIPPING_API_LEVEL))
-  else
-    min_systemsdk_version := $(PRODUCT_SHIPPING_API_LEVEL)
-  endif
   ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),)
     ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),)
       $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set)
diff --git a/core/main.mk b/core/main.mk
index 6095587..5142adc 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -83,6 +83,8 @@
 -include test/cts-root/tools/build/config.mk
 # WVTS-specific config.
 -include test/wvts/tools/build/config.mk
+# DTS-specific config.
+-include test/dts/tools/build/config.mk
 
 
 # Clean rules
@@ -290,7 +292,7 @@
 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
 
 # Build bootloader.img/radio.img, and unpack the partitions.
-include $(BUILD_SYSTEM)/tasks/tools/update_bootloader_radio_image.mk
+-include vendor/google/build/tasks/tools/update_bootloader_radio_image.mk
 
 # For an unbundled image, we can skip blueprint_tools because unbundled image
 # aims to remove a large number framework projects from the manifest, the
@@ -982,9 +984,7 @@
 # variables being set.
 define auto-included-modules
   $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver)) \
-  $(filter-out $(LLNDK_MOVED_TO_APEX_LIBRARIES),$(LLNDK_LIBRARIES)) \
   llndk.libraries.txt \
-  $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \
   $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \
   $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \
   $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \
diff --git a/core/product.mk b/core/product.mk
index 8fc40f8..7cf4ec2 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -366,6 +366,8 @@
 _product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE
 
 # List of boot jars delivered via updatable APEXes, following the same format as
 # PRODUCT_BOOT_JARS.
diff --git a/core/product_config.mk b/core/product_config.mk
index 738d4cf..abe6e38 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -424,10 +424,12 @@
   endif
 endif
 
-$(foreach pair,$(PRODUCT_APEX_BOOT_JARS), \
-  $(eval jar := $(call word-colon,2,$(pair))) \
-  $(if $(findstring $(jar), $(PRODUCT_BOOT_JARS)), \
-    $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is)))
+$(foreach apexpair,$(PRODUCT_APEX_BOOT_JARS), \
+  $(foreach platformpair,$(PRODUCT_BOOT_JARS), \
+    $(eval apexjar := $(call word-colon,2,$(apexpair))) \
+    $(eval platformjar := $(call word-colon,2,$(platformpair))) \
+    $(if $(filter $(apexjar), $(platformjar)), \
+      $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(apexjar) is))))
 
 ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT)
 ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)
@@ -602,7 +604,12 @@
     # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
     # In this case, the VSR API level is the minimum of the PRODUCT_SHIPPING_API_LEVEL
     # and RELEASE_BOARD_API_LEVEL
-    VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(RELEASE_BOARD_API_LEVEL))
+    board_api_level := $(RELEASE_BOARD_API_LEVEL)
+    ifdef BOARD_API_LEVEL_PROP_OVERRIDE
+      board_api_level := $(BOARD_API_LEVEL_PROP_OVERRIDE)
+    endif
+    VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(board_api_level))
+    board_api_level :=
   endif
 endif
 .KATI_READONLY := VSR_VENDOR_API_LEVEL
diff --git a/core/ravenwood_test_config_template.xml b/core/ravenwood_test_config_template.xml
index 2f21bae..9e9dd76 100644
--- a/core/ravenwood_test_config_template.xml
+++ b/core/ravenwood_test_config_template.xml
@@ -22,6 +22,7 @@
     <option name="use-ravenwood-resources" value="true" />
     <option name="exclude-paths" value="java" />
     <option name="null-device" value="true" />
+    <option name="do-not-swallow-runner-errors" value="true" />
 
     {EXTRA_CONFIGS}
 
diff --git a/core/soong_config.mk b/core/soong_config.mk
index a99bf0e..82be1f4 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -150,6 +150,7 @@
 $(call add_json_str,  BtConfigIncludeDir,                $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))
 $(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
 $(call add_json_str,  VendorApiLevel,                    $(BOARD_API_LEVEL))
+$(call add_json_str,  VendorApiLevelPropOverride,        $(BOARD_API_LEVEL_PROP_OVERRIDE))
 $(call add_json_list, ExtraVndkVersions,                 $(PRODUCT_EXTRA_VNDK_VERSIONS))
 $(call add_json_list, DeviceSystemSdkVersions,           $(BOARD_SYSTEMSDK_VERSIONS))
 $(call add_json_list, Platform_systemsdk_versions,       $(PLATFORM_SYSTEMSDK_VERSIONS))
@@ -182,9 +183,16 @@
 
 $(call add_json_bool, Uml,                               $(filter true,$(TARGET_USER_MODE_LINUX)))
 $(call add_json_str,  VendorPath,                        $(TARGET_COPY_OUT_VENDOR))
+$(call add_json_str,  VendorDlkmPath,                    $(TARGET_COPY_OUT_VENDOR_DLKM))
+$(call add_json_bool, BuildingVendorImage,               $(BUILDING_VENDOR_IMAGE))
 $(call add_json_str,  OdmPath,                           $(TARGET_COPY_OUT_ODM))
+$(call add_json_bool, BuildingOdmImage,                  $(BUILDING_ODM_IMAGE))
+$(call add_json_str,  OdmDlkmPath,                       $(TARGET_COPY_OUT_ODM_DLKM))
 $(call add_json_str,  ProductPath,                       $(TARGET_COPY_OUT_PRODUCT))
+$(call add_json_bool, BuildingProductImage,              $(BUILDING_PRODUCT_IMAGE))
 $(call add_json_str,  SystemExtPath,                     $(TARGET_COPY_OUT_SYSTEM_EXT))
+$(call add_json_str,  SystemDlkmPath,                    $(TARGET_COPY_OUT_SYSTEM_DLKM))
+$(call add_json_str,  OemPath,                           $(TARGET_COPY_OUT_OEM))
 $(call add_json_bool, MinimizeJavaDebugInfo,             $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO)))
 
 $(call add_json_bool, UseGoma,                           $(filter-out false,$(USE_GOMA)))
@@ -345,6 +353,7 @@
 $(call add_json_list, SystemExtPropFiles, $(TARGET_SYSTEM_EXT_PROP))
 $(call add_json_list, ProductPropFiles, $(TARGET_PRODUCT_PROP))
 $(call add_json_list, OdmPropFiles, $(TARGET_ODM_PROP))
+$(call add_json_list, VendorPropFiles, $(TARGET_VENDOR_PROP))
 
 $(call add_json_str, ExtraAllowedDepsTxt, $(EXTRA_ALLOWED_DEPS_TXT))
 
@@ -412,6 +421,21 @@
   $(call add_json_str, BoardExt4ShareDupBlocks, $(BOARD_EXT4_SHARE_DUP_BLOCKS))
   $(call add_json_str, BoardFlashLogicalBlockSize, $(BOARD_FLASH_LOGICAL_BLOCK_SIZE))
   $(call add_json_str, BoardFlashEraseBlockSize, $(BOARD_FLASH_ERASE_BLOCK_SIZE))
+  $(call add_json_bool, BuildingVbmetaImage, $(BUILDING_VBMETA_IMAGE))
+  $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE)))
+  $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_ALGORITHM))
+  $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_KEY_PATH))
+  $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_ROLLBACK_INDEX))
+  $(call add_json_map, ChainedVbmetaPartitions)
+  $(foreach partition,system vendor $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\
+    $(call add_json_map, $(partition)) \
+      $(call add_json_list,Partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))) \
+      $(call add_json_str,Key,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)) \
+      $(call add_json_str,Algorithm,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)) \
+      $(call add_json_str,RollbackIndex,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX)) \
+      $(call add_json_str,RollbackIndexLocation,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)) \
+    $(call end_json_map))
+  $(call end_json_map)
 
   $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))
   $(call add_json_bool, ProductUseDynamicPartitionSize, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE)))
@@ -420,8 +444,47 @@
   $(call add_json_list, ProductPackages, $(PRODUCT_PACKAGES))
   $(call add_json_list, ProductPackagesDebug, $(PRODUCT_PACKAGES_DEBUG))
 
+  # Used to generate /vendor/linker.config.pb
+  $(call add_json_list, VendorLinkerConfigSrcs, $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS))
+  $(call add_json_list, ProductLinkerConfigSrcs, $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS))
+
+  # Used to generate _dlkm partitions
+  $(call add_json_bool, BuildingSystemDlkmImage,               $(BUILDING_SYSTEM_DLKM_IMAGE))
+  $(call add_json_list, SystemKernelModules, $(BOARD_SYSTEM_KERNEL_MODULES))
+
+  # Used to generate /vendor/build.prop
+  $(call add_json_list, BoardInfoFiles, $(if $(TARGET_BOARD_INFO_FILES),$(TARGET_BOARD_INFO_FILES),$(firstword $(TARGET_BOARD_INFO_FILE) $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt))))
+  $(call add_json_str, BootLoaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME))
+
+  $(call add_json_map, ProductCopyFiles)
+  $(foreach pair,$(PRODUCT_COPY_FILES),\
+    $(call add_json_str,$(word 1,$(subst :, ,$(pair))),$(word 2,$(subst :, ,$(pair)))))
+  $(call end_json_map)
+
 $(call end_json_map)
 
+# For converting vintf_data
+$(call add_json_list, DeviceMatrixFile, $(DEVICE_MATRIX_FILE))
+$(call add_json_list, ProductManifestFiles, $(PRODUCT_MANIFEST_FILES))
+$(call add_json_list, SystemManifestFile, $(DEVICE_FRAMEWORK_MANIFEST_FILE))
+SYSTEM_EXT_HWSERVICE_FILES :=
+ifeq ($(PRODUCT_HIDL_ENABLED),true)
+  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager_no_max.xml
+  else
+    $(error If PRODUCT_HIDL_ENABLED is set, hwservicemanager must be added to PRODUCT_PACKAGES explicitly)
+  endif
+else
+  ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml
+  else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)),)
+    SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml
+  endif
+endif
+$(call add_json_list, SystemExtManifestFiles, $(SYSTEM_EXT_MANIFEST_FILES) $(SYSTEM_EXT_HWSERVICE_FILES))
+$(call add_json_list, DeviceManifestFiles, $(DEVICE_MANIFEST_FILE))
+$(call add_json_list, OdmManifestFiles, $(ODM_MANIFEST_FILES))
+
 $(call json_end)
 
 $(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk
index 00b5c0f..2ff83a1 100644
--- a/core/soong_extra_config.mk
+++ b/core/soong_extra_config.mk
@@ -43,6 +43,7 @@
 $(call add_json_list, PRODUCT_PRODUCT_PROPERTIES,        $(call collapse-prop-pairs,PRODUCT_PRODUCT_PROPERTIES))
 $(call add_json_list, PRODUCT_ODM_PROPERTIES,            $(call collapse-prop-pairs,PRODUCT_ODM_PROPERTIES))
 $(call add_json_list, PRODUCT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_PROPERTY_OVERRIDES))
+$(call add_json_list, PRODUCT_DEFAULT_PROPERTY_OVERRIDES,        $(call collapse-prop-pairs,PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
 
 $(call add_json_str, BootloaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME))
 
diff --git a/core/sysprop.mk b/core/sysprop.mk
index dc6f2c4..9b70d1c 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -281,51 +281,17 @@
 
 # ----------------------------------------------------------------
 # vendor_dlkm/etc/build.prop
-#
+# odm_dlkm/etc/build.prop
+# system_dlkm/build.prop
+# These are built by Soong. See build/soong/Android.bp
 
 INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop
-$(eval $(call build-properties,\
-    vendor_dlkm,\
-    $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty)))
-
-$(eval $(call declare-1p-target,$(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET)))
-
-# ----------------------------------------------------------------
-# odm_dlkm/etc/build.prop
-#
-
 INSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop
-$(eval $(call build-properties,\
-    odm_dlkm,\
-    $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty)))
-
-$(eval $(call declare-1p-target,$(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET)))
-
-# ----------------------------------------------------------------
-# system_dlkm/build.prop
-#
-
 INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_DLKM)/etc/build.prop
-$(eval $(call build-properties,\
-    system_dlkm,\
-    $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty),\
-    $(empty)))
-
-$(eval $(call declare-1p-target,$(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET)))
+ALL_DEFAULT_INSTALLED_MODULES += \
+  $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \
+  $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \
+  $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \
 
 # -----------------------------------------------------------------
 # system_ext/etc/build.prop
diff --git a/core/sysprop_config.mk b/core/sysprop_config.mk
index 6906611..1991503 100644
--- a/core/sysprop_config.mk
+++ b/core/sysprop_config.mk
@@ -91,8 +91,12 @@
 # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface.
 # This must not be altered outside of build system.
 ifdef BOARD_API_LEVEL
-ADDITIONAL_VENDOR_PROPERTIES += \
-    ro.board.api_level=$(BOARD_API_LEVEL)
+  ADDITIONAL_VENDOR_PROPERTIES += \
+    ro.board.api_level?=$(BOARD_API_LEVEL)
+  ifdef BOARD_API_LEVEL_PROP_OVERRIDE
+    ADDITIONAL_VENDOR_PROPERTIES += \
+      ro.board.api_level=$(BOARD_API_LEVEL_PROP_OVERRIDE)
+  endif
 endif
 # RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.
 ifdef RELEASE_BOARD_API_LEVEL_FROZEN
diff --git a/core/tasks/autorepro.mk b/core/tasks/autorepro.mk
new file mode 100644
index 0000000..2f81f9b
--- /dev/null
+++ b/core/tasks/autorepro.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+ifneq ($(wildcard test/sts/README-autorepro.md),)
+test_suite_name := autorepro
+test_suite_tradefed := sts-tradefed
+test_suite_readme := test/sts/README-autorepro.md
+autorepro_zip := $(HOST_OUT)/$(test_suite_name)/autorepro.zip
+
+include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
+
+autorepro_plugin_skel := $(call intermediates-dir-for,ETC,autorepro-plugin-skel.zip)/autorepro-plugin-skel.zip
+
+$(autorepro_zip): AUTOREPRO_ZIP := $(compatibility_zip)
+$(autorepro_zip): AUTOREPRO_PLUGIN_SKEL := $(autorepro_plugin_skel)
+$(autorepro_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(autorepro_plugin_skel)
+	rm -f $@ $(AUTOREPRO_ZIP)_filtered
+	$(ZIP2ZIP) -i $(AUTOREPRO_ZIP) -o $(AUTOREPRO_ZIP)_filtered \
+		-x android-autorepro/tools/sts-tradefed-tests.jar \
+		'android-autorepro/tools/*:autorepro/src/main/resources/sts-tradefed-tools/'
+	$(MERGE_ZIPS) $@ $(AUTOREPRO_ZIP)_filtered $(AUTOREPRO_PLUGIN_SKEL)
+	rm -f $(AUTOREPRO_ZIP)_filtered
+
+.PHONY: autorepro
+autorepro: $(autorepro_zip)
+$(call dist-for-goals, autorepro, $(autorepro_zip))
+
+endif
diff --git a/core/tasks/dts.mk b/core/tasks/dts.mk
new file mode 100644
index 0000000..8f09082
--- /dev/null
+++ b/core/tasks/dts.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Desktop test suite
+ifneq ($(wildcard test/dts/tools/dts-tradefed/README),)
+test_suite_name := dts
+test_suite_tradefed := dts-tradefed
+test_suite_readme := test/dts/tools/dts-tradefed/README
+test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \
+  $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar
+
+include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
+
+.PHONY: dts
+dts: $(compatibility_zip) $(compatibility_tests_list_zip)
+$(call dist-for-goals, dts, $(compatibility_zip) $(compatibility_tests_list_zip))
+endif
diff --git a/core/tasks/sts-sdk.mk b/core/tasks/sts-sdk.mk
deleted file mode 100644
index 4abbc29..0000000
--- a/core/tasks/sts-sdk.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-ifneq ($(wildcard test/sts/README-sts-sdk.md),)
-test_suite_name := sts-sdk
-test_suite_tradefed := sts-tradefed
-test_suite_readme := test/sts/README-sts-sdk.md
-sts_sdk_zip := $(HOST_OUT)/$(test_suite_name)/sts-sdk.zip
-
-include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
-
-sts_sdk_plugin_skel := $(call intermediates-dir-for,ETC,sts-sdk-plugin-skel.zip)/sts-sdk-plugin-skel.zip
-
-$(sts_sdk_zip): STS_SDK_ZIP := $(compatibility_zip)
-$(sts_sdk_zip): STS_SDK_PLUGIN_SKEL := $(sts_sdk_plugin_skel)
-$(sts_sdk_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(sts_sdk_plugin_skel)
-	rm -f $@ $(STS_SDK_ZIP)_filtered
-	$(ZIP2ZIP) -i $(STS_SDK_ZIP) -o $(STS_SDK_ZIP)_filtered \
-		-x android-sts-sdk/tools/sts-tradefed-tests.jar \
-		'android-sts-sdk/tools/*:sts-sdk/src/main/resources/sts-tradefed-tools/'
-	$(MERGE_ZIPS) $@ $(STS_SDK_ZIP)_filtered $(STS_SDK_PLUGIN_SKEL)
-	rm -f $(STS_SDK_ZIP)_filtered
-
-.PHONY: sts-sdk
-sts-sdk: $(sts_sdk_zip)
-$(call dist-for-goals, sts-sdk, $(sts_sdk_zip))
-
-endif
diff --git a/core/tasks/tools/update_bootloader_radio_image.mk b/core/tasks/tools/update_bootloader_radio_image.mk
deleted file mode 100644
index adb86ea..0000000
--- a/core/tasks/tools/update_bootloader_radio_image.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-ifeq ($(USES_DEVICE_GOOGLE_ZUMA),true)
-    -include vendor/google_devices/zuma/prebuilts/misc_bins/update_bootloader_radio_image.mk
-endif
-ifeq ($(USES_DEVICE_GOOGLE_ZUMAPRO),true)
-    -include vendor/google_devices/zumapro/prebuilts/misc_bins/update_bootloader_radio_image.mk
-endif
-ifeq ($(USES_DEVICE_GOOGLE_LAGUNA),true)
-    -include vendor/google_devices/laguna/prebuilts/misc_bins/update_bootloader_radio_image.mk
-endif
-ifeq ($(USES_DEVICE_GOOGLE_MALIBU),true)
-    -include vendor/google_devices/malibu/prebuilts/misc_bins/update_bootloader_radio_image.mk
-endif
diff --git a/envsetup.sh b/envsetup.sh
index 3fed5ae..554a220 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -362,7 +362,6 @@
       packages/modules/adb/adb.bash
       system/core/fastboot/fastboot.bash
       tools/asuite/asuite.sh
-      prebuilts/bazel/common/bazel-complete.bash
     )
     # Completion can be disabled selectively to allow users to use non-standard completion.
     # e.g.
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 8133af9..36be002 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -51,29 +51,6 @@
 
 # Copy compatibility metadata to the device.
 
-# Device Manifest
-ifdef DEVICE_MANIFEST_FILE
-# $(DEVICE_MANIFEST_FILE) can be a list of files
-include $(CLEAR_VARS)
-LOCAL_MODULE        := vendor_manifest.xml
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution
-LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
-LOCAL_MODULE_STEM   := manifest.xml
-LOCAL_MODULE_CLASS  := ETC
-LOCAL_MODULE_PATH   := $(TARGET_OUT_VENDOR)/etc/vintf
-
-GEN := $(local-generated-sources-dir)/manifest.xml
-$(GEN): PRIVATE_DEVICE_MANIFEST_FILE := $(DEVICE_MANIFEST_FILE)
-$(GEN): $(DEVICE_MANIFEST_FILE) $(HOST_OUT_EXECUTABLES)/assemble_vintf
-	BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \
-	PRODUCT_ENFORCE_VINTF_MANIFEST=$(PRODUCT_ENFORCE_VINTF_MANIFEST) \
-	$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \
-		-i $(call normalize-path-list,$(PRIVATE_DEVICE_MANIFEST_FILE))
-
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
-endif
-
 # DEVICE_MANIFEST_SKUS: a list of SKUS where DEVICE_MANIFEST_<sku>_FILES is defined.
 ifdef DEVICE_MANIFEST_SKUS
 
@@ -112,30 +89,6 @@
 
 endif # DEVICE_MANIFEST_SKUS
 
-# ODM manifest
-ifdef ODM_MANIFEST_FILES
-# ODM_MANIFEST_FILES is a list of files that is combined and installed as the default ODM manifest.
-include $(CLEAR_VARS)
-LOCAL_MODULE := odm_manifest.xml
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution
-LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
-LOCAL_MODULE_STEM := manifest.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := vintf
-LOCAL_ODM_MODULE := true
-
-GEN := $(local-generated-sources-dir)/manifest.xml
-$(GEN): PRIVATE_SRC_FILES := $(ODM_MANIFEST_FILES)
-$(GEN): $(ODM_MANIFEST_FILES) $(HOST_OUT_EXECUTABLES)/assemble_vintf
-	# Set VINTF_IGNORE_TARGET_FCM_VERSION to true because it should only be in device manifest.
-	VINTF_IGNORE_TARGET_FCM_VERSION=true \
-	$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \
-		-i $(call normalize-path-list,$(PRIVATE_SRC_FILES))
-
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
-endif # ODM_MANIFEST_FILES
-
 # ODM_MANIFEST_SKUS: a list of SKUS where ODM_MANIFEST_<sku>_FILES are defined.
 ifdef ODM_MANIFEST_SKUS
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 74ed82d..9d6402e 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -96,6 +96,7 @@
     enhanced-confirmation.xml \
     ExtShared \
     flags_health_check \
+    framework-connectivity-b \
     framework-graphics \
     framework-location \
     framework-minus-apex \
@@ -211,6 +212,7 @@
     libwilhelm \
     linker \
     llkd \
+    llndk_libs \
     lmkd \
     LocalTransport \
     locksettings \
@@ -274,7 +276,6 @@
     Shell \
     shell_and_utilities_system \
     sm \
-    snapshotctl \
     snapuserd \
     storaged \
     surfaceflinger \
@@ -287,6 +288,7 @@
     tombstoned \
     traced \
     traced_probes \
+    tradeinmode \
     tune2fs \
     uiautomator \
     uinput \
@@ -350,6 +352,11 @@
         com.android.ranging
 endif
 
+ifeq ($(RELEASE_MEMORY_MANAGEMENT_DAEMON),true)
+  PRODUCT_PACKAGES += \
+        mm_daemon
+endif
+
 # VINTF data for system image
 PRODUCT_PACKAGES += \
     system_manifest.xml \
@@ -424,6 +431,7 @@
     lpdump \
     mke2fs \
     mkfs.erofs \
+    pbtombstone \
     resize2fs \
     sgdisk \
     sqlite3 \
@@ -491,6 +499,7 @@
     record_binder \
     servicedispatcher \
     showmap \
+    snapshotctl \
     sqlite3 \
     ss \
     start_with_lockagent \
@@ -504,10 +513,6 @@
     unwind_reg_info \
     unwind_symbols \
 
-# For Remotely Provisioned Certificate Processor
-PRODUCT_SYSTEM_PROPERTIES += \
-    remote_provisioning.use_cert_processor=false
-
 # The set of packages whose code can be loaded by the system server.
 PRODUCT_SYSTEM_SERVER_APPS += \
     SettingsProvider \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 3f85941..16fc7fd 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -71,6 +71,8 @@
     passwd_odm \
     passwd_vendor \
     selinux_policy_nonsystem \
+    selinux_policy_vendor \
+    selinux_policy_odm \
     shell_and_utilities_vendor \
     odm-build.prop \
 
@@ -104,6 +106,7 @@
 # VINTF data for vendor image
 PRODUCT_PACKAGES += \
     vendor_compatibility_matrix.xml \
+    vendor_manifest.xml \
 
 # Base modules and settings for the debug ramdisk, which is then packed
 # into a boot-debug.img and a vendor_boot-debug.img.
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
index 9fc9ff9..7661e06 100644
--- a/target/product/build_variables.mk
+++ b/target/product/build_variables.mk
@@ -22,3 +22,6 @@
 
 # Use the configured release of sqlite
 $(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))
+
+# Use the configured MessageQueue implementation
+$(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION))
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 668f054..83d9215 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -51,6 +51,7 @@
     framework-minus-apex \
     framework-graphics \
     framework-location \
+    framework-connectivity-b \
     ext \
     telephony-common \
     voip-common \
diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp
index f86774b..c81e66c 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -385,7 +385,6 @@
         "android.software.webview.prebuilt.xml", // media_system
         "android.software.window_magnification.prebuilt.xml", // handheld_system
         "android.system.suspend-service",
-        "prebuilt_vintf_manifest",
         "apexd",
         "appops",
         "approved-ogki-builds.xml", // base_system
@@ -530,12 +529,14 @@
         "storaged", // base_system
         "surfaceflinger", // base_system
         "svc", // base_system
+        "system_manifest.xml", // base_system
         "task_profiles.json", // base_system
         "tc", // base_system
         "telecom", // base_system
         "tombstoned", // base_system
         "traced", // base_system
         "traced_probes", // base_system
+        "tradeinmode", // base_system
         "tune2fs", // base_system
         "uiautomator", // base_system
         "uinput", // base_system
@@ -562,6 +563,11 @@
             "trace_redactor", // base_system (RELEASE_PACKAGE_PROFILING_MODULE)
         ],
         default: [],
+    }) + select(release_flag("RELEASE_MEMORY_MANAGEMENT_DAEMON"), {
+        true: [
+            "mm_daemon", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON)
+        ],
+        default: [],
     }) + select(product_variable("debuggable"), {
         true: [
             "adevice_fingerprint",
@@ -695,6 +701,7 @@
                 "framework-graphics", // base_system
                 "framework-location", // base_system
                 "framework-minus-apex-install-dependencies", // base_system
+                "framework-connectivity-b", // base_system
                 "framework_compatibility_matrix.device.xml",
                 "generic_system_fonts", // ok
                 "hwservicemanager_compat_symlink_module", // base_system
@@ -785,6 +792,7 @@
                 "libbinder_ndk",
                 "libbinder_rpc_unstable",
                 "libcamera2ndk",
+                "libcgrouprc", // llndk library
                 "libclang_rt.asan",
                 "libcompiler_rt",
                 "libcutils", // used by many libs
@@ -861,11 +869,3 @@
         },
     },
 }
-
-prebuilt_etc {
-    name: "prebuilt_vintf_manifest",
-    src: "manifest.xml",
-    filename: "manifest.xml",
-    relative_install_path: "vintf",
-    no_full_install: true,
-}
diff --git a/target/product/generic/manifest.xml b/target/product/generic/manifest.xml
deleted file mode 100644
index 1df2c0d..0000000
--- a/target/product/generic/manifest.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<!--
-    Input:
-        system/libhidl/vintfdata/manifest.xml
--->
-<manifest version="8.0" type="framework">
-    <hal format="hidl" max-level="6">
-        <name>android.frameworks.displayservice</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.0::IDisplayService/default</fqname>
-    </hal>
-    <hal format="hidl" max-level="5">
-        <name>android.frameworks.schedulerservice</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.0::ISchedulingPolicyService/default</fqname>
-    </hal>
-    <hal format="aidl">
-        <name>android.frameworks.sensorservice</name>
-        <fqname>ISensorManager/default</fqname>
-    </hal>
-    <hal format="hidl" max-level="8">
-        <name>android.frameworks.sensorservice</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.0::ISensorManager/default</fqname>
-    </hal>
-    <hal format="hidl" max-level="8">
-        <name>android.hidl.memory</name>
-        <transport arch="32+64">passthrough</transport>
-        <fqname>@1.0::IMapper/ashmem</fqname>
-    </hal>
-    <hal format="hidl" max-level="7">
-        <name>android.system.net.netd</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.1::INetd/default</fqname>
-    </hal>
-    <hal format="hidl" max-level="7">
-        <name>android.system.wifi.keystore</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.0::IKeystore/default</fqname>
-    </hal>
-    <hal format="native">
-        <name>netutils-wrapper</name>
-        <version>1.0</version>
-    </hal>
-    <system-sdk>
-        <version>29</version>
-        <version>30</version>
-        <version>31</version>
-        <version>32</version>
-        <version>33</version>
-        <version>34</version>
-        <version>35</version>
-        <version>VanillaIceCream</version>
-    </system-sdk>
-</manifest>
diff --git a/target/product/generic_ramdisk.mk b/target/product/generic_ramdisk.mk
index ebac62f..388b180 100644
--- a/target/product/generic_ramdisk.mk
+++ b/target/product/generic_ramdisk.mk
@@ -35,8 +35,6 @@
 _my_paths := \
     $(TARGET_COPY_OUT_RAMDISK)/ \
     $(TARGET_COPY_OUT_DEBUG_RAMDISK)/ \
-    system/usr/share/zoneinfo/tz_version \
-    system/usr/share/zoneinfo/tzdata \
     $(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/system \
 
 
diff --git a/target/product/security/Android.bp b/target/product/security/Android.bp
index 69d19a3..ffbec06 100644
--- a/target/product/security/Android.bp
+++ b/target/product/security/Android.bp
@@ -40,4 +40,5 @@
 
 adb_keys {
     name: "adb_keys",
+    product_specific: true,
 }
diff --git a/target/product/virtual_ab_ota/compression.mk b/target/product/virtual_ab_ota/compression.mk
index dc1ee3e..e77c36f 100644
--- a/target/product/virtual_ab_ota/compression.mk
+++ b/target/product/virtual_ab_ota/compression.mk
@@ -18,9 +18,12 @@
 
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true
-PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true
 
+# Optional assignment. On low memory devices, disabling io_uring can relieve cpu and memory
+# pressure during an OTA.
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true
+
 # Enabling this property, will improve OTA install time
 # but will use an additional CPU core
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true
diff --git a/target/product/virtual_ab_ota/vabc_features.mk b/target/product/virtual_ab_ota/vabc_features.mk
index e2745a1..d092699 100644
--- a/target/product/virtual_ab_ota/vabc_features.mk
+++ b/target/product/virtual_ab_ota/vabc_features.mk
@@ -31,14 +31,15 @@
 
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true
-PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true
-PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true
+
+# Optional assignments, low memory devices may benefit from overriding these.
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled?=true
+
 # Low memory device configurations. If memory usage and cpu utilization is
 # a bottleneck during OTA, the below configurations can be added to a
-# device's .mk file improve performance for low mem devices. Disabling
-# ro.virtual_ab.compression.xor.enabled and ro.virtual_ab.io_uring.enabled
-# is also recommended
+# device's .mk file improve performance for low mem devices.
 #
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.read_ahead_size=16
 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.o_direct.enabled=true
diff --git a/teams/Android.bp b/teams/Android.bp
index 4c40287..21f5222 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -13,6 +13,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// DON'T ADD NEW RULES HERE. For more details refer to
+// go/new-android-ownership-model
+
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
@@ -4475,3 +4478,13 @@
     // go/trendy/manage/engineers/6303298703949824
     trendy_team_id: "6303298703949824",
 }
+
+team {
+    name: "trendy_team_desktop_stats",
+
+    // go/trendy/manage/engineers/5440764114206720
+    trendy_team_id: "5440764114206720",
+}
+
+// DON'T ADD NEW RULES HERE. For more details refer to
+// go/new-android-ownership-model
diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
index f4dd103..5e3eb12 100644
--- a/tools/aconfig/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -68,6 +68,14 @@
     ],
 }
 
+aconfig_values {
+    name: "aconfig.test.flag.second_values",
+    package: "com.android.aconfig.test",
+    srcs: [
+        "tests/third.values",
+    ],
+}
+
 aconfig_value_set {
     name: "aconfig.test.flag.value_set",
     values: [
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 47d4042..bfdf1a7 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -501,7 +501,7 @@
             modified_parsed_flags.into_iter(),
             mode,
             flag_ids,
-            false,
+            true,
         )
         .unwrap();
         let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
@@ -513,6 +513,7 @@
         package com.android.aconfig.test;
         // TODO(b/303773055): Remove the annotation after access issue is resolved.
         import android.compat.annotation.UnsupportedAppUsage;
+        import android.os.Binder;
         import android.provider.DeviceConfig;
         import android.provider.DeviceConfig.Properties;
         import android.aconfig.storage.StorageInternalReader;
@@ -544,6 +545,7 @@
                 isCached = true;
             }
             private void load_overrides_aconfig_test() {
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     Properties properties = DeviceConfig.getProperties("aconfig_test");
                     disabledRw =
@@ -561,11 +563,16 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
                 aconfig_test_is_cached = true;
             }
 
             private void load_overrides_other_namespace() {
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     Properties properties = DeviceConfig.getProperties("other_namespace");
                     disabledRwInOtherNamespace =
@@ -579,6 +586,10 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
                 other_namespace_is_cached = true;
             }
@@ -760,6 +771,7 @@
 
         let expect_feature_flags_impl_content = r#"
         package com.android.aconfig.test;
+        import android.os.Binder;
         import android.provider.DeviceConfig;
         import android.provider.DeviceConfig.Properties;
         /** @hide */
@@ -770,6 +782,7 @@
             private static boolean enabledRoExported = false;
 
             private void load_overrides_aconfig_test() {
+                final long ident = Binder.clearCallingIdentity();
                 try {
                     Properties properties = DeviceConfig.getProperties("aconfig_test");
                     disabledRwExported =
@@ -787,6 +800,10 @@
                         + "flag declaration.",
                         e
                     );
+                } catch (SecurityException e) {
+                    // for isolated process case, skip loading flag value from the storage, use the default
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
                 aconfig_test_is_cached = true;
             }
diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
index d318b96..569a34b 100644
--- a/tools/aconfig/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -297,7 +297,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -309,7 +309,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -344,7 +344,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -356,7 +356,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -391,7 +391,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(false)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -403,7 +403,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return false;
             }
         }
     } else {
@@ -439,7 +439,7 @@
                             },
                             None => {
                                 log!(Level::Error, "no context found for package com.android.aconfig.test");
-                                Ok(true)
+                                Err(format!("failed to flag package com.android.aconfig.test"))
                             }
                         }
                     })
@@ -451,7 +451,7 @@
             },
             Err(err) => {
                 log!(Level::Error, "aconfig_rust_codegen: error: {err}");
-                panic!("failed to read flag value: {err}");
+                return true;
             }
         }
     } else {
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 496876e..0ad3d97 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -17,7 +17,7 @@
 use anyhow::{bail, ensure, Context, Result};
 use itertools::Itertools;
 use protobuf::Message;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::HashMap;
 use std::hash::Hasher;
 use std::io::Read;
 use std::path::PathBuf;
@@ -425,23 +425,34 @@
 
 #[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
                     // protect hardcoded offset reads.
-pub fn compute_flag_offsets_fingerprint(flags_map: &HashMap<String, u16>) -> Result<u64> {
+                    // Creates a fingerprint of the flag names (which requires sorting the vector).
+                    // Fingerprint is used by both codegen and storage files.
+pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> Result<u64> {
+    flag_names.sort();
+
     let mut hasher = SipHasher13::new();
-
-    // Need to sort to ensure the data is added to the hasher in the same order
-    // each run.
-    let sorted_map: BTreeMap<&String, &u16> = flags_map.iter().collect();
-
-    for (flag, offset) in sorted_map {
-        // See https://docs.rs/siphasher/latest/siphasher/#note for use of write
-        // over write_i16. Similarly, use to_be_bytes rather than to_ne_bytes to
-        // ensure consistency.
+    for flag in flag_names {
         hasher.write(flag.as_bytes());
-        hasher.write(&offset.to_be_bytes());
     }
     Ok(hasher.finish())
 }
 
+#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to
+                    // protect hardcoded offset reads.
+                    // Converts ProtoParsedFlags into a vector of strings containing all of the flag
+                    // names. Helper fn for creating fingerprint for codegen files. Flags must all
+                    // belong to the same package.
+fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> {
+    let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>();
+
+    // All flags must belong to the same package as the fingerprint is per-package.
+    let Some(_package) = find_unique_package(&separated_flags) else {
+        bail!("No parsed flags, or the parsed flags use different packages.");
+    };
+
+    Ok(separated_flags.into_iter().map(|flag| flag.name.unwrap()).collect::<Vec<_>>())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -450,16 +461,51 @@
     #[test]
     fn test_offset_fingerprint() {
         let parsed_flags = crate::test::parse_test_flags();
-        let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string();
-        let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap();
-        let expected_fingerprint = 10709892481002252132u64;
+        let expected_fingerprint: u64 = 5801144784618221668;
 
-        let hash_result = compute_flag_offsets_fingerprint(&flag_ids);
+        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
+        let hash_result = compute_flags_fingerprint(&mut extracted_flags);
 
         assert_eq!(hash_result.unwrap(), expected_fingerprint);
     }
 
     #[test]
+    fn test_offset_fingerprint_matches_from_package() {
+        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();
+
+        // All test flags are in the same package, so fingerprint from all of them.
+        let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap();
+        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags);
+
+        let mut flag_names_vec = parsed_flags
+            .parsed_flag
+            .clone()
+            .into_iter()
+            .map(|flag| flag.name.unwrap())
+            .map(String::from)
+            .collect::<Vec<_>>();
+        let result_from_names = compute_flags_fingerprint(&mut flag_names_vec);
+
+        // Assert the same hash is generated for each case.
+        assert_eq!(result_from_parsed_flags.unwrap(), result_from_names.unwrap());
+    }
+
+    #[test]
+    fn test_offset_fingerprint_different_packages_does_not_match() {
+        // Parse flags from two packages.
+        let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags();
+        let second_parsed_flags = crate::test::parse_second_package_flags();
+
+        let mut extracted_flags = extract_flag_names(parsed_flags).unwrap();
+        let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags).unwrap();
+        let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap();
+        let second_result = compute_flags_fingerprint(&mut second_extracted_flags).unwrap();
+
+        // Different flags should have a different fingerprint.
+        assert_ne!(result_from_parsed_flags, second_result);
+    }
+
+    #[test]
     fn test_parse_flags() {
         let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
         aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index edb4fd3..e184efe 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -51,8 +51,7 @@
         .subcommand(
             Command::new("create-cache")
                 .arg(Arg::new("package").long("package").required(true))
-                // TODO(b/312769710): Make this argument required.
-                .arg(Arg::new("container").long("container"))
+                .arg(Arg::new("container").long("container").required(true))
                 .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append))
                 .arg(Arg::new("values").long("values").action(ArgAction::Append))
                 .arg(
diff --git a/tools/aconfig/aconfig/src/storage/flag_info.rs b/tools/aconfig/aconfig/src/storage/flag_info.rs
index 2532609..5d565e8 100644
--- a/tools/aconfig/aconfig/src/storage/flag_info.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_info.rs
@@ -83,7 +83,7 @@
         let flag_info_list = create_test_flag_info_list_from_source();
         assert!(flag_info_list.is_ok());
         let expected_flag_info_list =
-            aconfig_storage_file::test_utils::create_test_flag_info_list();
+            aconfig_storage_file::test_utils::create_test_flag_info_list(DEFAULT_FILE_VERSION);
         assert_eq!(flag_info_list.unwrap(), expected_flag_info_list);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
index 6046d7e..8856eb6 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -157,7 +157,8 @@
     fn test_table_contents() {
         let flag_table = create_test_flag_table_from_source();
         assert!(flag_table.is_ok());
-        let expected_flag_table = aconfig_storage_file::test_utils::create_test_flag_table();
+        let expected_flag_table =
+            aconfig_storage_file::test_utils::create_test_flag_table(DEFAULT_FILE_VERSION);
         assert_eq!(flag_table.unwrap(), expected_flag_table);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
index 6a655b9..0dd5a9d 100644
--- a/tools/aconfig/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -82,7 +82,7 @@
         let flag_value_list = create_test_flag_value_list_from_source();
         assert!(flag_value_list.is_ok());
         let expected_flag_value_list =
-            aconfig_storage_file::test_utils::create_test_flag_value_list();
+            aconfig_storage_file::test_utils::create_test_flag_value_list(DEFAULT_FILE_VERSION);
         assert_eq!(flag_value_list.unwrap(), expected_flag_value_list);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 9e5dad5..c7fd55a 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -32,6 +32,7 @@
 pub struct FlagPackage<'a> {
     pub package_name: &'a str,
     pub package_id: u32,
+    pub fingerprint: u64,
     pub flag_names: HashSet<&'a str>,
     pub boolean_flags: Vec<&'a ProtoParsedFlag>,
     // The index of the first boolean flag in this aconfig package among all boolean
@@ -44,6 +45,7 @@
         FlagPackage {
             package_name,
             package_id,
+            fingerprint: 0,
             flag_names: HashSet::new(),
             boolean_flags: vec![],
             boolean_start_index: 0,
@@ -79,6 +81,8 @@
     for p in packages.iter_mut() {
         p.boolean_start_index = boolean_start_index;
         boolean_start_index += p.boolean_flags.len() as u32;
+
+        // TODO: b/316357686 - Calculate fingerprint and add to package.
     }
 
     packages
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
index 56559f8..e46607b 100644
--- a/tools/aconfig/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -47,6 +47,7 @@
         let node = PackageTableNode {
             package_name: String::from(package.package_name),
             package_id: package.package_id,
+            fingerprint: package.fingerprint,
             boolean_start_index: package.boolean_start_index,
             next_offset: None,
         };
@@ -127,7 +128,8 @@
     fn test_table_contents() {
         let package_table = create_test_package_table_from_source();
         assert!(package_table.is_ok());
-        let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table();
+        let expected_package_table =
+            aconfig_storage_file::test_utils::create_test_package_table(DEFAULT_FILE_VERSION);
         assert_eq!(package_table.unwrap(), expected_package_table);
     }
 }
diff --git a/tools/aconfig/aconfig/src/test.rs b/tools/aconfig/aconfig/src/test.rs
index 7409cda..a19b372 100644
--- a/tools/aconfig/aconfig/src/test.rs
+++ b/tools/aconfig/aconfig/src/test.rs
@@ -295,6 +295,24 @@
         aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
     }
 
+    pub fn parse_second_package_flags() -> ProtoParsedFlags {
+        let bytes = crate::commands::parse_flags(
+            "com.android.aconfig.second_test",
+            Some("system"),
+            vec![Input {
+                source: "tests/test_second_package.aconfig".to_string(),
+                reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()),
+            }],
+            vec![Input {
+                source: "tests/third.values".to_string(),
+                reader: Box::new(include_bytes!("../tests/third.values").as_slice()),
+            }],
+            crate::commands::DEFAULT_FLAG_PERMISSION,
+        )
+        .unwrap();
+        aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
+    }
+
     pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
         let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
         let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 26d3069..cb52150 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -1,11 +1,13 @@
 package {package_name};
 {{ -if not is_test_mode }}
+{{ -if allow_instrumentation }}
 {{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
 {{ -endif }}
 
 {{ -if runtime_lookup_required }}
+import android.os.Binder;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
 
@@ -56,6 +58,7 @@
 
 {{ for namespace_with_flags in namespace_flags }}
     private void load_overrides_{namespace_with_flags.namespace}() \{
+        final long ident = Binder.clearCallingIdentity();
         try \{
             Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
 {{ -for flag in namespace_with_flags.flags }}
@@ -73,6 +76,10 @@
                 + "flag declaration.",
                 e
             );
+        } catch (SecurityException e) \{
+            // for isolated process case, skip loading flag value from the storage, use the default
+        } finally \{
+            Binder.restoreCallingIdentity(ident);
         }
         {namespace_with_flags.namespace}_is_cached = true;
     }
@@ -110,6 +117,76 @@
     }
 {{ endfor }}
 }
+
+{{ else }} {#- else for allow_instrumentation is not enabled #}
+{{ if not library_exported- }}
+// TODO(b/303773055): Remove the annotation after access issue is resolved.
+import android.compat.annotation.UnsupportedAppUsage;
+{{ -endif }}
+
+{{ -if runtime_lookup_required }}
+import android.os.Binder;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+{{ -endif }}
+/** @hide */
+public final class FeatureFlagsImpl implements FeatureFlags \{
+{{ -if runtime_lookup_required }}
+{{ -for namespace_with_flags in namespace_flags }}
+    private static volatile boolean {namespace_with_flags.namespace}_is_cached = false;
+{{ -endfor- }}
+
+{{ for flag in flag_elements }}
+{{- if flag.is_read_write }}
+    private static boolean {flag.method_name} = {flag.default_value};
+{{ -endif }}
+{{ -endfor }}
+{{ for namespace_with_flags in namespace_flags }}
+    private void load_overrides_{namespace_with_flags.namespace}() \{
+        final long ident = Binder.clearCallingIdentity();
+        try \{
+            Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
+{{ -for flag in namespace_with_flags.flags }}
+{{ -if flag.is_read_write }}
+            {flag.method_name} =
+                properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value});
+{{ -endif }}
+{{ -endfor }}
+        } catch (NullPointerException e) \{
+            throw new RuntimeException(
+                "Cannot read value from namespace {namespace_with_flags.namespace} "
+                + "from DeviceConfig. It could be that the code using flag "
+                + "executed before SettingsProvider initialization. Please use "
+                + "fixed read-only flag by adding is_fixed_read_only: true in "
+                + "flag declaration.",
+                e
+            );
+        } finally \{
+            Binder.restoreCallingIdentity(ident);
+        }
+        {namespace_with_flags.namespace}_is_cached = true;
+}
+{{ endfor- }}
+{{ -endif }}{#- end of runtime_lookup_required #}
+{{ -for flag in flag_elements }}
+    @Override
+{{ -if not library_exported }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
+    @UnsupportedAppUsage
+{{ -endif }}
+    public boolean {flag.method_name}() \{
+{{ -if flag.is_read_write }}
+        if (!{flag.device_config_namespace}_is_cached) \{
+            load_overrides_{flag.device_config_namespace}();
+        }
+        return {flag.method_name};
+{{ -else }}
+        return {flag.default_value};
+{{ -endif }}
+    }
+{{ endfor }}
+}
+{{ endif}} {#- endif for allow_instrumentation #}
 {{ else }} {#- Generate only stub if in test mode #}
 /** @hide */
 public final class FeatureFlagsImpl implements FeatureFlags \{
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index eaaf86f..df3b10d 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -92,17 +92,21 @@
                  aconfig_storage::StorageFileType::package_map);
             if (!package_map_file.ok()) \{
                 ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             auto context = aconfig_storage::get_package_read_context(
                 **package_map_file, "{package}");
             if (!context.ok()) \{
                 ALOGE("error: failed to get package read context: %s", context.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             if (!(context->package_exists)) \{
-               package_exists_in_storage_ = false;
-               return;
+                package_exists_in_storage_ = false;
+                return;
             }
 
             // cache package boolean flag start index
@@ -116,6 +120,8 @@
                 aconfig_storage::StorageFileType::flag_val);
             if (!flag_value_file.ok()) \{
                 ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str());
+                package_exists_in_storage_ = false;
+                return;
             }
 
             // cache flag value file
diff --git a/tools/aconfig/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template
index 6456360..d0079d4 100644
--- a/tools/aconfig/aconfig/templates/rust.template
+++ b/tools/aconfig/aconfig/templates/rust.template
@@ -53,7 +53,7 @@
                             },
                             None => \{
                                  log!(Level::Error, "no context found for package {package}");
-                                 Ok({flag.default_value})
+                                 Err(format!("failed to flag package {package}"))
                             }
                         }
                     })
@@ -65,7 +65,7 @@
             },
             Err(err) => \{
                 log!(Level::Error, "aconfig_rust_codegen: error: \{err}");
-                panic!("failed to read flag value: \{err}");
+                return {flag.default_value};
             }
         }
     } else \{
diff --git a/tools/aconfig/aconfig/tests/test_second_package.aconfig b/tools/aconfig/aconfig/tests/test_second_package.aconfig
new file mode 100644
index 0000000..188bc96
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/test_second_package.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.aconfig.second_test"
+container: "system"
+
+flag {
+    name: "testing_flag"
+    namespace: "another_namespace"
+    description: "This is a flag for testing."
+    bug: "123"
+}
+
diff --git a/tools/aconfig/aconfig/tests/third.values b/tools/aconfig/aconfig/tests/third.values
new file mode 100644
index 0000000..675832a
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/third.values
@@ -0,0 +1,6 @@
+flag_value {
+    package: "com.android.aconfig.second_test"
+    name: "testing_flag"
+    state: DISABLED
+    permission: READ_WRITE
+}
diff --git a/tools/aconfig/aconfig_flags/flags.aconfig b/tools/aconfig/aconfig_flags/flags.aconfig
index 0a004ca..b66d282 100644
--- a/tools/aconfig/aconfig_flags/flags.aconfig
+++ b/tools/aconfig/aconfig_flags/flags.aconfig
@@ -14,3 +14,11 @@
   bug: "369808805"
   description: "When enabled, launch aconfigd from config infra module."
 }
+
+flag {
+  name: "enable_system_aconfigd_rust"
+  namespace: "core_experiments_team_internal"
+  bug: "378079539"
+  description: "When enabled, the aconfigd cc_binary target becomes a no-op, and the rust_binary aconfigd-system target starts up."
+  is_fixed_read_only: true
+}
diff --git a/tools/aconfig/aconfig_flags/src/lib.rs b/tools/aconfig/aconfig_flags/src/lib.rs
index 2e89127..b413c62 100644
--- a/tools/aconfig/aconfig_flags/src/lib.rs
+++ b/tools/aconfig/aconfig_flags/src/lib.rs
@@ -39,6 +39,11 @@
     pub fn enable_aconfigd_from_mainline() -> bool {
         aconfig_flags_rust::enable_only_new_storage()
     }
+
+    /// Returns the value for the enable_system_aconfigd_rust flag.
+    pub fn enable_system_aconfigd_rust() -> bool {
+        aconfig_flags_rust::enable_system_aconfigd_rust()
+    }
 }
 
 /// Module used when building with cargo
@@ -55,4 +60,10 @@
         // Used only to enable typechecking and testing with cargo
         true
     }
+
+    /// Returns a placeholder value for the enable_system_aconfigd_rust flag.
+    pub fn enable_system_aconfigd_rust() -> bool {
+        // Used only to enable typechecking and testing with cargo
+        true
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
index f090396..cf16834 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -194,12 +194,15 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::create_test_flag_info_list;
+    use crate::{
+        test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,
+    };
 
-    #[test]
     // this test point locks down the value list serialization
-    fn test_serialization() {
-        let flag_info_list = create_test_flag_info_list();
+    // TODO: b/376108268 - Use parameterized tests.
+    #[test]
+    fn test_serialization_default() {
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
 
         let header: &FlagInfoHeader = &flag_info_list.header;
         let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
@@ -220,20 +223,42 @@
     }
 
     #[test]
-    // this test point locks down that version number should be at the top of serialized
-    // bytes
-    fn test_version_number() {
-        let flag_info_list = create_test_flag_info_list();
-        let bytes = &flag_info_list.into_bytes();
-        let mut head = 0;
-        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1);
+    fn test_serialization_max() {
+        let flag_info_list = create_test_flag_info_list(MAX_SUPPORTED_FILE_VERSION);
+
+        let header: &FlagInfoHeader = &flag_info_list.header;
+        let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
+        assert!(reinterpreted_header.is_ok());
+        assert_eq!(header, &reinterpreted_header.unwrap());
+
+        let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
+        for node in nodes.iter() {
+            let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();
+            assert_eq!(node, &reinterpreted_node);
+        }
+
+        let flag_info_bytes = flag_info_list.into_bytes();
+        let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
+        assert!(reinterpreted_info_list.is_ok());
+        assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
+        assert_eq!(flag_info_bytes.len() as u32, header.file_size);
     }
 
+    // this test point locks down that version number should be at the top of serialized
+    // bytes
     #[test]
+    fn test_version_number() {
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
+        let bytes = &flag_info_list.into_bytes();
+        let mut head = 0;
+        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);
+    }
+
     // this test point locks down file type check
+    #[test]
     fn test_file_type_check() {
-        let mut flag_info_list = create_test_flag_info_list();
+        let mut flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
         flag_info_list.header.file_type = 123u8;
         let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err();
         assert_eq!(
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index 0588fe5..6fbee02 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -220,12 +220,15 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::create_test_flag_table;
+    use crate::{
+        test_utils::create_test_flag_table, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,
+    };
 
-    #[test]
     // this test point locks down the table serialization
-    fn test_serialization() {
-        let flag_table = create_test_flag_table();
+    // TODO: b/376108268 - Use parameterized tests.
+    #[test]
+    fn test_serialization_default() {
+        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);
 
         let header: &FlagTableHeader = &flag_table.header;
         let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
@@ -246,20 +249,42 @@
     }
 
     #[test]
-    // this test point locks down that version number should be at the top of serialized
-    // bytes
-    fn test_version_number() {
-        let flag_table = create_test_flag_table();
-        let bytes = &flag_table.into_bytes();
-        let mut head = 0;
-        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1);
+    fn test_serialization_max() {
+        let flag_table = create_test_flag_table(MAX_SUPPORTED_FILE_VERSION);
+
+        let header: &FlagTableHeader = &flag_table.header;
+        let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
+        assert!(reinterpreted_header.is_ok());
+        assert_eq!(header, &reinterpreted_header.unwrap());
+
+        let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
+        for node in nodes.iter() {
+            let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
+            assert_eq!(node, &reinterpreted_node);
+        }
+
+        let flag_table_bytes = flag_table.into_bytes();
+        let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);
+        assert!(reinterpreted_table.is_ok());
+        assert_eq!(&flag_table, &reinterpreted_table.unwrap());
+        assert_eq!(flag_table_bytes.len() as u32, header.file_size);
     }
 
+    // this test point locks down that version number should be at the top of serialized
+    // bytes
     #[test]
+    fn test_version_number() {
+        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);
+        let bytes = &flag_table.into_bytes();
+        let mut head = 0;
+        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);
+    }
+
     // this test point locks down file type check
+    #[test]
     fn test_file_type_check() {
-        let mut flag_table = create_test_flag_table();
+        let mut flag_table = create_test_flag_table(DEFAULT_FILE_VERSION);
         flag_table.header.file_type = 123u8;
         let error = FlagTable::from_bytes(&flag_table.into_bytes()).unwrap_err();
         assert_eq!(
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
index b64c10e..9a14bec 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -132,12 +132,32 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::create_test_flag_value_list;
+    use crate::{
+        test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION,
+    };
 
     #[test]
     // this test point locks down the value list serialization
-    fn test_serialization() {
-        let flag_value_list = create_test_flag_value_list();
+    // TODO: b/376108268 - Use parameterized tests.
+    fn test_serialization_default() {
+        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
+
+        let header: &FlagValueHeader = &flag_value_list.header;
+        let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
+        assert!(reinterpreted_header.is_ok());
+        assert_eq!(header, &reinterpreted_header.unwrap());
+
+        let flag_value_bytes = flag_value_list.into_bytes();
+        let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
+        assert!(reinterpreted_value_list.is_ok());
+        assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
+        assert_eq!(flag_value_bytes.len() as u32, header.file_size);
+    }
+
+    #[test]
+    // this test point locks down the value list serialization
+    fn test_serialization_max() {
+        let flag_value_list = create_test_flag_value_list(MAX_SUPPORTED_FILE_VERSION);
 
         let header: &FlagValueHeader = &flag_value_list.header;
         let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
@@ -155,17 +175,17 @@
     // this test point locks down that version number should be at the top of serialized
     // bytes
     fn test_version_number() {
-        let flag_value_list = create_test_flag_value_list();
+        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
         let bytes = &flag_value_list.into_bytes();
         let mut head = 0;
-        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1);
+        let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version_from_file, DEFAULT_FILE_VERSION);
     }
 
     #[test]
     // this test point locks down file type check
     fn test_file_type_check() {
-        let mut flag_value_list = create_test_flag_value_list();
+        let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
         flag_value_list.header.file_type = 123u8;
         let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err();
         assert_eq!(
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 1d92ba4..1e5b001 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -228,10 +228,14 @@
 
 /// Read and parse bytes as u8
 pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {
-    let val =
-        u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| {
-            BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg))
-        })?);
+    let val = u8::from_le_bytes(
+        buf.get(*head..*head + 1)
+            .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+                "fail to parse u8 from bytes: access out of bounds"
+            )))?
+            .try_into()
+            .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg)))?,
+    );
     *head += 1;
     Ok(val)
 }
@@ -241,10 +245,16 @@
     buf: &[u8],
     head: &mut usize,
 ) -> Result<u16, AconfigStorageError> {
-    let val =
-        u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| {
-            BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
-        })?);
+    let val = u16::from_le_bytes(
+        buf.get(*head..*head + 2)
+            .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+                "fail to parse u16 from bytes: access out of bounds"
+            )))?
+            .try_into()
+            .map_err(|errmsg| {
+                BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
+            })?,
+    );
     *head += 2;
     Ok(val)
 }
@@ -256,20 +266,32 @@
 
 /// Read and parse bytes as u32
 pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError> {
-    let val =
-        u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| {
-            BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
-        })?);
+    let val = u32::from_le_bytes(
+        buf.get(*head..*head + 4)
+            .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+                "fail to parse u32 from bytes: access out of bounds"
+            )))?
+            .try_into()
+            .map_err(|errmsg| {
+                BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
+            })?,
+    );
     *head += 4;
     Ok(val)
 }
 
 // Read and parse bytes as u64
 pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> {
-    let val =
-        u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| {
-            BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg))
-        })?);
+    let val = u64::from_le_bytes(
+        buf.get(*head..*head + 8)
+            .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+                "fail to parse u64 from bytes: access out of bounds"
+            )))?
+            .try_into()
+            .map_err(|errmsg| {
+                BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg))
+            })?,
+    );
     *head += 8;
     Ok(val)
 }
@@ -280,8 +302,21 @@
     head: &mut usize,
 ) -> Result<String, AconfigStorageError> {
     let num_bytes = read_u32_from_bytes(buf, head)? as usize;
-    let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())
-        .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
+    // TODO(opg): Document this limitation and check it when creating files.
+    if num_bytes > 1024 {
+        return Err(AconfigStorageError::BytesParseFail(anyhow!(
+            "fail to parse string from bytes, string is too long (found {}, max is 1024)",
+            num_bytes
+        )));
+    }
+    let val = String::from_utf8(
+        buf.get(*head..*head + num_bytes)
+            .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+                "fail to parse string from bytes: access out of bounds"
+            )))?
+            .to_vec(),
+    )
+    .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
     *head += num_bytes;
     Ok(val)
 }
@@ -533,13 +568,46 @@
     };
 
     #[test]
+    fn test_list_flags_with_missing_files_error() {
+        let flag_list_error = list_flags("does", "not", "exist").unwrap_err();
+        assert_eq!(
+            format!("{:?}", flag_list_error),
+            format!(
+                "FileReadFail(Failed to open file does: No such file or directory (os error 2))"
+            )
+        );
+    }
+
+    #[test]
+    fn test_list_flags_with_invalid_files_error() {
+        let invalid_bytes: [u8; 3] = [0; 3];
+        let package_table = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+        let flag_table = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+        let flag_value_list = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+        let package_table_path = package_table.path().display().to_string();
+        let flag_table_path = flag_table.path().display().to_string();
+        let flag_value_list_path = flag_value_list.path().display().to_string();
+        let flag_list_error =
+            list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap_err();
+        assert_eq!(
+            format!("{:?}", flag_list_error),
+            format!("BytesParseFail(fail to parse u32 from bytes: access out of bounds)")
+        );
+    }
+
+    #[test]
     // this test point locks down the flag list api
     fn test_list_flag() {
         let package_table =
-            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
-        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
-        let flag_value_list =
-            write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
+            write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())
+                .unwrap();
+        let flag_table =
+            write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())
+                .unwrap();
+        let flag_value_list = write_bytes_to_temp_file(
+            &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),
+        )
+        .unwrap();
 
         let package_table_path = package_table.path().display().to_string();
         let flag_table_path = flag_table.path().display().to_string();
@@ -604,12 +672,19 @@
     // this test point locks down the flag list with info api
     fn test_list_flag_with_info() {
         let package_table =
-            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
-        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
-        let flag_value_list =
-            write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
-        let flag_info_list =
-            write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap();
+            write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes())
+                .unwrap();
+        let flag_table =
+            write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes())
+                .unwrap();
+        let flag_value_list = write_bytes_to_temp_file(
+            &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(),
+        )
+        .unwrap();
+        let flag_info_list = write_bytes_to_temp_file(
+            &create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(),
+        )
+        .unwrap();
 
         let package_table_path = package_table.path().display().to_string();
         let flag_table_path = flag_table.path().display().to_string();
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index af39fbc..21357c7 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -100,6 +100,7 @@
 pub struct PackageTableNode {
     pub package_name: String,
     pub package_id: u32,
+    pub fingerprint: u64,
     // The index of the first boolean flag in this aconfig package among all boolean
     // flags in this container.
     pub boolean_start_index: u32,
@@ -111,8 +112,12 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Package: {}, Id: {}, Boolean flag start index: {}, Next: {:?}",
-            self.package_name, self.package_id, self.boolean_start_index, self.next_offset
+            "Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}",
+            self.package_name,
+            self.package_id,
+            self.fingerprint,
+            self.boolean_start_index,
+            self.next_offset
         )?;
         Ok(())
     }
@@ -146,9 +151,7 @@
         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(name_bytes);
         result.extend_from_slice(&self.package_id.to_le_bytes());
-        // V2 storage files have a fingerprint. Current struct (v1) does not, so
-        // we write 0.
-        result.extend_from_slice(&0u64.to_le_bytes());
+        result.extend_from_slice(&self.fingerprint.to_le_bytes());
         result.extend_from_slice(&self.boolean_start_index.to_le_bytes());
         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
         result
@@ -172,13 +175,15 @@
         let mut head = 0;
         let package_name = read_str_from_bytes(bytes, &mut head)?;
         let package_id = read_u32_from_bytes(bytes, &mut head)?;
+        // v1 does not have fingerprint, so just set to 0.
+        let fingerprint: u64 = 0;
         let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;
         let next_offset = match read_u32_from_bytes(bytes, &mut head)? {
             0 => None,
             val => Some(val),
         };
 
-        let node = Self { package_name, package_id, boolean_start_index, next_offset };
+        let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };
         Ok(node)
     }
 
@@ -186,18 +191,14 @@
         let mut head = 0;
         let package_name = read_str_from_bytes(bytes, &mut head)?;
         let package_id = read_u32_from_bytes(bytes, &mut head)?;
-
-        // Fingerprint is unused in the current struct (v1), but we need to read
-        // the bytes if the storage file type is v2 or else the subsequent
-        // fields will be inaccurate.
-        let _fingerprint = read_u64_from_bytes(bytes, &mut head)?;
+        let fingerprint = read_u64_from_bytes(bytes, &mut head)?;
         let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;
         let next_offset = match read_u32_from_bytes(bytes, &mut head)? {
             0 => None,
             val => Some(val),
         };
 
-        let node = Self { package_name, package_id, boolean_start_index, next_offset };
+        let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };
         Ok(node)
     }
 
@@ -281,13 +282,37 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::read_u32_from_start_of_bytes;
-    use crate::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION};
+    use crate::test_utils::create_test_package_table;
+    use crate::{read_u32_from_start_of_bytes, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION};
 
     #[test]
     // this test point locks down the table serialization
-    fn test_serialization() {
-        let package_table = create_test_package_table();
+    // TODO: b/376108268 - Use parameterized tests.
+    fn test_serialization_default() {
+        let package_table = create_test_package_table(DEFAULT_FILE_VERSION);
+        let header: &PackageTableHeader = &package_table.header;
+        let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
+        assert!(reinterpreted_header.is_ok());
+        assert_eq!(header, &reinterpreted_header.unwrap());
+
+        let nodes: &Vec<PackageTableNode> = &package_table.nodes;
+        for node in nodes.iter() {
+            let reinterpreted_node =
+                PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)
+                    .unwrap();
+            assert_eq!(node, &reinterpreted_node);
+        }
+
+        let package_table_bytes = package_table.into_bytes();
+        let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
+        assert!(reinterpreted_table.is_ok());
+        assert_eq!(&package_table, &reinterpreted_table.unwrap());
+        assert_eq!(package_table_bytes.len() as u32, header.file_size);
+    }
+
+    #[test]
+    fn test_serialization_max() {
+        let package_table = create_test_package_table(MAX_SUPPORTED_FILE_VERSION);
         let header: &PackageTableHeader = &package_table.header;
         let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
         assert!(reinterpreted_header.is_ok());
@@ -312,44 +337,36 @@
     // this test point locks down that version number should be at the top of serialized
     // bytes
     fn test_version_number() {
-        let package_table = create_test_package_table();
+        let package_table = create_test_package_table(DEFAULT_FILE_VERSION);
         let bytes = &package_table.into_bytes();
-        let version = read_u32_from_start_of_bytes(bytes).unwrap();
-        assert_eq!(version, DEFAULT_FILE_VERSION);
+        let unpacked_version = read_u32_from_start_of_bytes(bytes).unwrap();
+        assert_eq!(unpacked_version, DEFAULT_FILE_VERSION);
     }
 
     #[test]
-    fn test_round_trip_v1() {
-        let table_v1: PackageTable = create_test_package_table();
-        let table_bytes_v1 = table_v1.into_bytes();
+    fn test_round_trip_default() {
+        let table: PackageTable = create_test_package_table(DEFAULT_FILE_VERSION);
+        let table_bytes = table.into_bytes();
 
-        // Will automatically read from version 2 as the version code is encoded
-        // into the bytes.
-        let reinterpreted_table = PackageTable::from_bytes(&table_bytes_v1).unwrap();
+        let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();
 
-        assert_eq!(table_v1, reinterpreted_table);
+        assert_eq!(table, reinterpreted_table);
     }
 
     #[test]
-    fn test_round_trip_v2() {
-        // Have to fake v2 because though we will set the version to v2
-        // and write the bytes as v2, we don't have the ability to actually set
-        // the fingerprint yet.
-        let mut fake_table_v2 = create_test_package_table();
-        fake_table_v2.header.version = 2;
-        let table_bytes_v2 = fake_table_v2.into_bytes();
+    fn test_round_trip_max() {
+        let table: PackageTable = create_test_package_table(MAX_SUPPORTED_FILE_VERSION);
+        let table_bytes = table.into_bytes();
 
-        // Will automatically read from version 2 as the version code is encoded
-        // into the bytes.
-        let reinterpreted_table = PackageTable::from_bytes(&table_bytes_v2).unwrap();
+        let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();
 
-        assert_eq!(fake_table_v2, reinterpreted_table);
+        assert_eq!(table, reinterpreted_table);
     }
 
     #[test]
     // this test point locks down file type check
     fn test_file_type_check() {
-        let mut package_table = create_test_package_table();
+        let mut package_table = create_test_package_table(DEFAULT_FILE_VERSION);
         package_table.header.file_type = 123u8;
         let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err();
         assert_eq!(
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 5c364f6..55780ed 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -18,38 +18,53 @@
 use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
 use crate::flag_value::{FlagValueHeader, FlagValueList};
 use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
-use crate::{AconfigStorageError, StorageFileType, StoredFlagType, DEFAULT_FILE_VERSION};
+use crate::{AconfigStorageError, StorageFileType, StoredFlagType};
 
 use anyhow::anyhow;
 use std::io::Write;
 use tempfile::NamedTempFile;
 
-pub fn create_test_package_table() -> PackageTable {
+pub fn create_test_package_table(version: u32) -> PackageTable {
     let header = PackageTableHeader {
-        version: DEFAULT_FILE_VERSION,
+        version: version,
         container: String::from("mockup"),
         file_type: StorageFileType::PackageMap as u8,
-        file_size: 209,
+        file_size: match version {
+            1 => 209,
+            2 => 233,
+            _ => panic!("Unsupported version."),
+        },
         num_packages: 3,
         bucket_offset: 31,
         node_offset: 59,
     };
-    let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None];
+    let buckets: Vec<Option<u32>> = match version {
+        1 => vec![Some(59), None, None, Some(109), None, None, None],
+        2 => vec![Some(59), None, None, Some(117), None, None, None],
+        _ => panic!("Unsupported version."),
+    };
     let first_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_2"),
         package_id: 1,
+        fingerprint: 0,
         boolean_start_index: 3,
         next_offset: None,
     };
     let second_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_1"),
         package_id: 0,
+        fingerprint: 0,
         boolean_start_index: 0,
-        next_offset: Some(159),
+        next_offset: match version {
+            1 => Some(159),
+            2 => Some(175),
+            _ => panic!("Unsupported version."),
+        },
     };
     let third_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_4"),
         package_id: 2,
+        fingerprint: 0,
         boolean_start_index: 6,
         next_offset: None,
     };
@@ -76,9 +91,9 @@
     }
 }
 
-pub fn create_test_flag_table() -> FlagTable {
+pub fn create_test_flag_table(version: u32) -> FlagTable {
     let header = FlagTableHeader {
-        version: DEFAULT_FILE_VERSION,
+        version: version,
         container: String::from("mockup"),
         file_type: StorageFileType::FlagMap as u8,
         file_size: 321,
@@ -118,9 +133,9 @@
     FlagTable { header, buckets, nodes }
 }
 
-pub fn create_test_flag_value_list() -> FlagValueList {
+pub fn create_test_flag_value_list(version: u32) -> FlagValueList {
     let header = FlagValueHeader {
-        version: DEFAULT_FILE_VERSION,
+        version: version,
         container: String::from("mockup"),
         file_type: StorageFileType::FlagVal as u8,
         file_size: 35,
@@ -131,9 +146,9 @@
     FlagValueList { header, booleans }
 }
 
-pub fn create_test_flag_info_list() -> FlagInfoList {
+pub fn create_test_flag_info_list(version: u32) -> FlagInfoList {
     let header = FlagInfoHeader {
-        version: DEFAULT_FILE_VERSION,
+        version: version,
         container: String::from("mockup"),
         file_type: StorageFileType::FlagInfo as u8,
         file_size: 35,
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
index 86a75f2..b1c7ee7 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
@@ -16,12 +16,118 @@
 
 package android.aconfig.storage;
 
+/**
+ * Exception thrown when an error occurs while accessing Aconfig Storage.
+ *
+ * <p>This exception indicates a general problem with Aconfig Storage, such as an inability to read
+ * or write data.
+ */
 public class AconfigStorageException extends RuntimeException {
+
+    /** Generic error code indicating an unspecified Aconfig Storage error. */
+    public static final int ERROR_GENERIC = 0;
+
+    /** Error code indicating that the Aconfig Storage system is not found on the device. */
+    public static final int ERROR_STORAGE_SYSTEM_NOT_FOUND = 1;
+
+    /** Error code indicating that the requested configuration package is not found. */
+    public static final int ERROR_PACKAGE_NOT_FOUND = 2;
+
+    /** Error code indicating that the specified container is not found. */
+    public static final int ERROR_CONTAINER_NOT_FOUND = 3;
+
+    /** Error code indicating that there was an error reading the Aconfig Storage file. */
+    public static final int ERROR_CANNOT_READ_STORAGE_FILE = 4;
+
+    private final int mErrorCode;
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with a generic error code and the specified
+     * detail message.
+     *
+     * @param msg The detail message for this exception.
+     */
     public AconfigStorageException(String msg) {
         super(msg);
+        mErrorCode = ERROR_GENERIC;
     }
 
+    /**
+     * Constructs a new {@code AconfigStorageException} with a generic error code, the specified
+     * detail message, and cause.
+     *
+     * @param msg The detail message for this exception.
+     * @param cause The cause of this exception.
+     */
     public AconfigStorageException(String msg, Throwable cause) {
         super(msg, cause);
+        mErrorCode = ERROR_GENERIC;
+    }
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with the specified error code and detail
+     * message.
+     *
+     * @param errorCode The error code for this exception.
+     * @param msg The detail message for this exception.
+     */
+    public AconfigStorageException(int errorCode, String msg) {
+        super(msg);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Constructs a new {@code AconfigStorageException} with the specified error code, detail
+     * message, and cause.
+     *
+     * @param errorCode The error code for this exception.
+     * @param msg The detail message for this exception.
+     * @param cause The cause of this exception.
+     */
+    public AconfigStorageException(int errorCode, String msg, Throwable cause) {
+        super(msg, cause);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Returns the error code associated with this exception.
+     *
+     * @return The error code.
+     */
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    /**
+     * Returns the error message for this exception, including the error code and the original
+     * message.
+     *
+     * @return The error message.
+     */
+    @Override
+    public String getMessage() {
+        return errorString() + ": " + super.getMessage();
+    }
+
+    /**
+     * Returns a string representation of the error code.
+     *
+     * @return The error code string.
+     */
+    private String errorString() {
+        switch (mErrorCode) {
+            case ERROR_GENERIC:
+                return "ERROR_GENERIC";
+            case ERROR_STORAGE_SYSTEM_NOT_FOUND:
+                return "ERROR_STORAGE_SYSTEM_NOT_FOUND";
+            case ERROR_PACKAGE_NOT_FOUND:
+                return "ERROR_PACKAGE_NOT_FOUND";
+            case ERROR_CONTAINER_NOT_FOUND:
+                return "ERROR_CONTAINER_NOT_FOUND";
+            case ERROR_CANNOT_READ_STORAGE_FILE:
+                return "ERROR_CANNOT_READ_STORAGE_FILE";
+            default:
+                return "<Unknown error code " + mErrorCode + ">";
+        }
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
index 4bea083..9571568 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
@@ -41,8 +41,16 @@
         return this.mByteBuffer.getInt();
     }
 
+    public long readLong() {
+        return this.mByteBuffer.getLong();
+    }
+
     public String readString() {
         int length = readInt();
+        if (length > 1024) {
+            throw new AconfigStorageException(
+                    "String length exceeds maximum allowed size (1024 bytes): " + length);
+        }
         byte[] bytes = new byte[length];
         mByteBuffer.get(bytes, 0, length);
         return new String(bytes, StandardCharsets.UTF_8);
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
index 39b7e59..a45d12a 100644
--- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
@@ -124,8 +124,10 @@
 
         private String mPackageName;
         private int mPackageId;
+        private long mPackageFingerprint;
         private int mBooleanStartIndex;
         private int mNextOffset;
+        private boolean mHasPackageFingerprint;
 
         private static Node fromBytes(ByteBufferReader reader, int version) {
             switch (version) {
@@ -153,9 +155,11 @@
             Node node = new Node();
             node.mPackageName = reader.readString();
             node.mPackageId = reader.readInt();
+            node.mPackageFingerprint = reader.readLong();
             node.mBooleanStartIndex = reader.readInt();
             node.mNextOffset = reader.readInt();
             node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+            node.mHasPackageFingerprint = true;
             return node;
         }
 
@@ -189,6 +193,10 @@
             return mPackageId;
         }
 
+        public long getPackageFingerprint() {
+            return mPackageFingerprint;
+        }
+
         public int getBooleanStartIndex() {
             return mBooleanStartIndex;
         }
@@ -196,5 +204,9 @@
         public int getNextOffset() {
             return mNextOffset;
         }
+
+        public boolean hasPackageFingerprint() {
+            return mHasPackageFingerprint;
+        }
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
index 13d3214..bd46d5f 100644
--- a/tools/aconfig/aconfig_storage_file/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -10,10 +10,14 @@
         "libbase",
     ],
     data: [
-        "data/v1/package.map",
-        "data/v1/flag.map",
-        "data/v1/flag.val",
-        "data/v1/flag.info",
+        "data/v1/package_v1.map",
+        "data/v1/flag_v1.map",
+        "data/v1/flag_v1.val",
+        "data/v1/flag_v1.info",
+        "data/v2/package_v2.map",
+        "data/v2/flag_v2.map",
+        "data/v2/flag_v2.val",
+        "data/v2/flag_v2.info",
     ],
     test_suites: [
         "device-tests",
@@ -35,10 +39,14 @@
     test_config: "AndroidStorageJaveTest.xml",
     sdk_version: "test_current",
     data: [
-        "data/v1/package.map",
-        "data/v1/flag.map",
-        "data/v1/flag.val",
-        "data/v1/flag.info",
+        "data/v1/package_v1.map",
+        "data/v1/flag_v1.map",
+        "data/v1/flag_v1.val",
+        "data/v1/flag_v1.info",
+        "data/v2/package_v2.map",
+        "data/v2/flag_v2.map",
+        "data/v2/flag_v2.val",
+        "data/v2/flag_v2.info",
     ],
     test_suites: [
         "general-tests",
diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
index 2d52d44..20fbfdb 100644
--- a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
+++ b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
@@ -21,13 +21,17 @@
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
-        <option name="push" value="package.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package.map" />
-        <option name="push" value="flag.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.map" />
-        <option name="push" value="flag.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.val" />
-        <option name="push" value="flag.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.info" />
+        <option name="push" value="package_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package_v1.map" />
+        <option name="push" value="flag_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.map" />
+        <option name="push" value="flag_v1.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.val" />
+        <option name="push" value="flag_v1.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.info" />
+        <option name="push" value="package_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package_v2.map" />
+        <option name="push" value="flag_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.map" />
+        <option name="push" value="flag_v2.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.val" />
+        <option name="push" value="flag_v2.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.info" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.aconfig.storage.test" />
         <option name="runtime-hint" value="1m" />
     </test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.info b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.info
similarity index 100%
rename from tools/aconfig/aconfig_storage_file/tests/data/v1/flag.info
rename to tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.map b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.map
similarity index 100%
rename from tools/aconfig/aconfig_storage_file/tests/data/v1/flag.map
rename to tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.val b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.val
similarity index 100%
rename from tools/aconfig/aconfig_storage_file/tests/data/v1/flag.val
rename to tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/package.map b/tools/aconfig/aconfig_storage_file/tests/data/v1/package_v1.map
similarity index 100%
rename from tools/aconfig/aconfig_storage_file/tests/data/v1/package.map
rename to tools/aconfig/aconfig_storage_file/tests/data/v1/package_v1.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info
new file mode 100644
index 0000000..9db7fde
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map
new file mode 100644
index 0000000..cf4685c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val
new file mode 100644
index 0000000..37d4750
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map b/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map
new file mode 100644
index 0000000..16f4054
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
index fd40d4c..dc465b6 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
@@ -31,7 +31,7 @@
 
     @Test
     public void testFlagTable_rightHeader() throws Exception {
-        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));
         FlagTable.Header header = flagTable.getHeader();
         assertEquals(1, header.getVersion());
         assertEquals("mockup", header.getContainer());
@@ -44,7 +44,7 @@
 
     @Test
     public void testFlagTable_rightNode() throws Exception {
-        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));
 
         FlagTable.Node node1 = flagTable.get(0, "enabled_ro");
         FlagTable.Node node2 = flagTable.get(0, "enabled_rw");
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
index 1b0de63..306df7d 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
@@ -34,7 +34,7 @@
     @Test
     public void testFlagValueList_rightHeader() throws Exception {
         FlagValueList flagValueList =
-                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));
         FlagValueList.Header header = flagValueList.getHeader();
         assertEquals(1, header.getVersion());
         assertEquals("mockup", header.getContainer());
@@ -47,7 +47,7 @@
     @Test
     public void testFlagValueList_rightNode() throws Exception {
         FlagValueList flagValueList =
-                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));
 
         boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true};
         assertEquals(expected.length, flagValueList.size());
@@ -60,11 +60,11 @@
     @Test
     public void testFlagValueList_getValue() throws Exception {
         PackageTable packageTable =
-                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
-        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1));
 
         FlagValueList flagValueList =
-                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1));
 
         PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1");
         FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw");
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
index e7e19d8..4d7ab2a 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
@@ -17,6 +17,8 @@
 package android.aconfig.storage.test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import android.aconfig.storage.FileType;
 import android.aconfig.storage.PackageTable;
@@ -31,7 +33,7 @@
     @Test
     public void testPackageTable_rightHeader() throws Exception {
         PackageTable packageTable =
-                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));
         PackageTable.Header header = packageTable.getHeader();
         assertEquals(1, header.getVersion());
         assertEquals("mockup", header.getContainer());
@@ -43,9 +45,23 @@
     }
 
     @Test
+    public void testPackageTable_rightHeader_v2() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));
+        PackageTable.Header header = packageTable.getHeader();
+        assertEquals(2, header.getVersion());
+        assertEquals("mockup", header.getContainer());
+        assertEquals(FileType.PACKAGE_MAP, header.getFileType());
+        assertEquals(233, header.getFileSize());
+        assertEquals(3, header.getNumPackages());
+        assertEquals(31, header.getBucketOffset());
+        assertEquals(59, header.getNodeOffset());
+    }
+
+    @Test
     public void testPackageTable_rightNode() throws Exception {
         PackageTable packageTable =
-                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1));
 
         PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
         PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2");
@@ -66,5 +82,39 @@
         assertEquals(159, node1.getNextOffset());
         assertEquals(-1, node2.getNextOffset());
         assertEquals(-1, node4.getNextOffset());
+
+        assertFalse(node1.hasPackageFingerprint());
+        assertFalse(node2.hasPackageFingerprint());
+        assertFalse(node4.hasPackageFingerprint());
+    }
+
+    @Test
+    public void testPackageTable_rightNode_v2() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2));
+
+        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
+        PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2");
+        PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4");
+
+        assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName());
+        assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName());
+        assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName());
+
+        assertEquals(0, node1.getPackageId());
+        assertEquals(1, node2.getPackageId());
+        assertEquals(2, node4.getPackageId());
+
+        assertEquals(0, node1.getBooleanStartIndex());
+        assertEquals(3, node2.getBooleanStartIndex());
+        assertEquals(6, node4.getBooleanStartIndex());
+
+        assertEquals(175, node1.getNextOffset());
+        assertEquals(-1, node2.getNextOffset());
+        assertEquals(-1, node4.getNextOffset());
+
+        assertTrue(node1.hasPackageFingerprint());
+        assertTrue(node2.hasPackageFingerprint());
+        assertTrue(node4.hasPackageFingerprint());
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
index f35952d..0643154 100644
--- a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
@@ -21,28 +21,28 @@
 import java.nio.ByteBuffer;
 
 public final class TestDataUtils {
-    private static final String TEST_PACKAGE_MAP_PATH = "package.map";
-    private static final String TEST_FLAG_MAP_PATH = "flag.map";
-    private static final String TEST_FLAG_VAL_PATH = "flag.val";
-    private static final String TEST_FLAG_INFO_PATH = "flag.info";
+    private static final String TEST_PACKAGE_MAP_PATH = "package_v%d.map";
+    private static final String TEST_FLAG_MAP_PATH = "flag_v%d.map";
+    private static final String TEST_FLAG_VAL_PATH = "flag_v%d.val";
+    private static final String TEST_FLAG_INFO_PATH = "flag_v%d.info";
 
     private static final String TESTDATA_PATH =
             "/data/local/tmp/aconfig_storage_file_test_java/testdata/";
 
-    public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
-        return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
+    public static ByteBuffer getTestPackageMapByteBuffer(int version) throws Exception {
+        return readFile(TESTDATA_PATH + String.format(TEST_PACKAGE_MAP_PATH, version));
     }
 
-    public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
-        return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
+    public static ByteBuffer getTestFlagMapByteBuffer(int version) throws Exception {
+        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_MAP_PATH, version));
     }
 
-    public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
-        return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
+    public static ByteBuffer getTestFlagValByteBuffer(int version) throws Exception {
+        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_VAL_PATH, version));
     }
 
-    public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
-        return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
+    public static ByteBuffer getTestFlagInfoByteBuffer(int version) throws Exception {
+        return readFile(TESTDATA_PATH + String.format(TEST_FLAG_INFO_PATH, version));
     }
 
     private static ByteBuffer readFile(String fileName) throws Exception {
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
index 3626f72..5c008af 100644
--- a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
+++ b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
@@ -24,10 +24,8 @@
 using namespace android::base;
 using namespace aconfig_storage;
 
-void verify_value(const FlagValueSummary& flag,
-                  const std::string& package_name,
-                  const std::string& flag_name,
-                  const std::string& flag_val,
+void verify_value(const FlagValueSummary& flag, const std::string& package_name,
+                  const std::string& flag_name, const std::string& flag_val,
                   const std::string& value_type) {
   ASSERT_EQ(flag.package_name, package_name);
   ASSERT_EQ(flag.flag_name, flag_name);
@@ -39,10 +37,8 @@
                        const std::string& package_name,
                        const std::string& flag_name,
                        const std::string& flag_val,
-                       const std::string& value_type,
-                       bool is_readwrite,
-                       bool has_server_override,
-                       bool has_local_override) {
+                       const std::string& value_type, bool is_readwrite,
+                       bool has_server_override, bool has_local_override) {
   ASSERT_EQ(flag.package_name, package_name);
   ASSERT_EQ(flag.flag_name, flag_name);
   ASSERT_EQ(flag.flag_value, flag_val);
@@ -52,63 +48,137 @@
   ASSERT_EQ(flag.has_local_override, has_local_override);
 }
 
-TEST(AconfigStorageFileTest, test_list_flag) {
+Result<std::vector<FlagValueSummary>> get_flag_list_result(
+    const std::string version) {
   auto const test_base_dir = GetExecutableDirectory();
-  auto const test_dir = test_base_dir + "/data/v1";
-  auto const package_map = test_dir + "/package.map";
-  auto const flag_map = test_dir + "/flag.map";
-  auto const flag_val = test_dir + "/flag.val";
-  auto flag_list_result = aconfig_storage::list_flags(
-      package_map, flag_map, flag_val);
+  auto const test_dir = test_base_dir + "/data/v" + version;
+  auto const package_map = test_dir + "/package_v" + version + ".map";
+  auto const flag_map = test_dir + "/flag_v" + version + ".map";
+  auto const flag_val = test_dir + "/flag_v" + version + ".val";
+  return aconfig_storage::list_flags(package_map, flag_map, flag_val);
+}
+
+Result<std::vector<FlagValueAndInfoSummary>> get_flag_list_result_with_info(
+    const std::string version) {
+  auto const test_base_dir = GetExecutableDirectory();
+  auto const test_dir = test_base_dir + "/data/v" + version;
+  auto const package_map = test_dir + "/package_v" + version + ".map";
+  auto const flag_map = test_dir + "/flag_v" + version + ".map";
+  auto const flag_val = test_dir + "/flag_v" + version + ".val";
+  auto const flag_info = test_dir + "/flag_v" + version + ".info";
+  return aconfig_storage::list_flags_with_info(package_map, flag_map, flag_val,
+                                               flag_info);
+}
+
+TEST(AconfigStorageFileTest, test_list_flag) {
+  auto flag_list_result = get_flag_list_result("1");
   ASSERT_TRUE(flag_list_result.ok());
 
   auto const& flag_list = *flag_list_result;
   ASSERT_EQ(flag_list.size(), 8);
-  verify_value(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
-               "false", "ReadWriteBoolean");
+  verify_value(flag_list[0], "com.android.aconfig.storage.test_1",
+               "disabled_rw", "false", "ReadWriteBoolean");
   verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
                "true", "ReadOnlyBoolean");
   verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
                "true", "ReadWriteBoolean");
-  verify_value(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
-               "false", "ReadWriteBoolean");
-  verify_value(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
-               "true", "FixedReadOnlyBoolean");
+  verify_value(flag_list[3], "com.android.aconfig.storage.test_2",
+               "disabled_rw", "false", "ReadWriteBoolean");
+  verify_value(flag_list[4], "com.android.aconfig.storage.test_2",
+               "enabled_fixed_ro", "true", "FixedReadOnlyBoolean");
   verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
                "true", "ReadOnlyBoolean");
-  verify_value(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
-               "true", "FixedReadOnlyBoolean");
+  verify_value(flag_list[6], "com.android.aconfig.storage.test_4",
+               "enabled_fixed_ro", "true", "FixedReadOnlyBoolean");
+  verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
+               "true", "ReadWriteBoolean");
+}
+
+// TODO: b/376256472 - Use parameterized tests.
+TEST(AconfigStorageFileTest, test_list_flag_v2) {
+  auto flag_list_result = get_flag_list_result("2");
+  ASSERT_TRUE(flag_list_result.ok());
+
+  auto const& flag_list = *flag_list_result;
+  ASSERT_EQ(flag_list.size(), 8);
+  verify_value(flag_list[0], "com.android.aconfig.storage.test_1",
+               "disabled_rw", "false", "ReadWriteBoolean");
+  verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
+               "true", "ReadOnlyBoolean");
+  verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
+               "true", "ReadWriteBoolean");
+  verify_value(flag_list[3], "com.android.aconfig.storage.test_2",
+               "disabled_rw", "false", "ReadWriteBoolean");
+  verify_value(flag_list[4], "com.android.aconfig.storage.test_2",
+               "enabled_fixed_ro", "true", "FixedReadOnlyBoolean");
+  verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
+               "true", "ReadOnlyBoolean");
+  verify_value(flag_list[6], "com.android.aconfig.storage.test_4",
+               "enabled_fixed_ro", "true", "FixedReadOnlyBoolean");
   verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
                "true", "ReadWriteBoolean");
 }
 
 TEST(AconfigStorageFileTest, test_list_flag_with_info) {
-  auto const base_test_dir = GetExecutableDirectory();
-  auto const test_dir = base_test_dir + "/data/v1";
-  auto const package_map = test_dir + "/package.map";
-  auto const flag_map = test_dir + "/flag.map";
-  auto const flag_val = test_dir + "/flag.val";
-  auto const flag_info = test_dir + "/flag.info";
-  auto flag_list_result = aconfig_storage::list_flags_with_info(
-      package_map, flag_map, flag_val, flag_info);
+  auto flag_list_result = get_flag_list_result_with_info("1");
   ASSERT_TRUE(flag_list_result.ok());
 
   auto const& flag_list = *flag_list_result;
   ASSERT_EQ(flag_list.size(), 8);
-  verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
-                    "false", "ReadWriteBoolean", true, false, false);
-  verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
-                    "true", "ReadOnlyBoolean", false, false, false);
-  verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
-                    "true", "ReadWriteBoolean", true, false, false);
-  verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
-                    "false", "ReadWriteBoolean", true, false, false);
-  verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
-                    "true", "FixedReadOnlyBoolean", false, false, false);
-  verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
-                    "true", "ReadOnlyBoolean", false, false, false);
-  verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
-                    "true", "FixedReadOnlyBoolean", false, false, false);
-  verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
-                    "true", "ReadWriteBoolean", true, false, false);
+  verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1",
+                    "disabled_rw", "false", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1",
+                    "enabled_ro", "true", "ReadOnlyBoolean", false, false,
+                    false);
+  verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1",
+                    "enabled_rw", "true", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2",
+                    "disabled_rw", "false", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2",
+                    "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false,
+                    false, false);
+  verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2",
+                    "enabled_ro", "true", "ReadOnlyBoolean", false, false,
+                    false);
+  verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4",
+                    "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false,
+                    false, false);
+  verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4",
+                    "enabled_rw", "true", "ReadWriteBoolean", true, false,
+                    false);
+}
+
+TEST(AconfigStorageFileTest, test_list_flag_with_info_v2) {
+  auto flag_list_result = get_flag_list_result_with_info("2");
+  ASSERT_TRUE(flag_list_result.ok());
+
+  auto const& flag_list = *flag_list_result;
+  ASSERT_EQ(flag_list.size(), 8);
+  verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1",
+                    "disabled_rw", "false", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1",
+                    "enabled_ro", "true", "ReadOnlyBoolean", false, false,
+                    false);
+  verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1",
+                    "enabled_rw", "true", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2",
+                    "disabled_rw", "false", "ReadWriteBoolean", true, false,
+                    false);
+  verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2",
+                    "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false,
+                    false, false);
+  verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2",
+                    "enabled_ro", "true", "ReadOnlyBoolean", false, false,
+                    false);
+  verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4",
+                    "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false,
+                    false, false);
+  verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4",
+                    "enabled_rw", "true", "ReadWriteBoolean", true, false,
+                    false);
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 80b8ece..7f7dd5a 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -36,10 +36,10 @@
         "librand",
     ],
     data: [
-        "tests/data/v1/package.map",
-        "tests/data/v1/flag.map",
-        "tests/data/v1/flag.val",
-        "tests/data/v1/flag.info",
+        "tests/data/v1/package_v1.map",
+        "tests/data/v1/flag_v1.map",
+        "tests/data/v1/flag_v1.val",
+        "tests/data/v1/flag_v1.info",
     ],
 }
 
@@ -154,6 +154,8 @@
 java_library {
     name: "aconfig_storage_reader_java",
     srcs: [
+        "srcs/android/aconfig/storage/AconfigPackageImpl.java",
+        "srcs/android/aconfig/storage/StorageFileProvider.java",
         "srcs/android/aconfig/storage/StorageInternalReader.java",
     ],
     libs: [
@@ -175,6 +177,8 @@
 java_library {
     name: "aconfig_storage_reader_java_none",
     srcs: [
+        "srcs/android/aconfig/storage/AconfigPackageImpl.java",
+        "srcs/android/aconfig/storage/StorageFileProvider.java",
         "srcs/android/aconfig/storage/StorageInternalReader.java",
     ],
     libs: [
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
index fe57a6d..68b6193 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
@@ -55,12 +55,14 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit};
+    use aconfig_storage_file::{
+        test_utils::create_test_flag_info_list, FlagInfoBit, DEFAULT_FILE_VERSION,
+    };
 
     #[test]
     // this test point locks down query if flag has server override
     fn test_is_flag_sticky() {
-        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();
         for offset in 0..8 {
             let attribute =
                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
@@ -71,7 +73,7 @@
     #[test]
     // this test point locks down query if flag is readwrite
     fn test_is_flag_readwrite() {
-        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();
         let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for offset in 0..8 {
             let attribute =
@@ -86,7 +88,7 @@
     #[test]
     // this test point locks down query if flag has local override
     fn test_flag_has_override() {
-        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();
         for offset in 0..8 {
             let attribute =
                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
@@ -97,7 +99,7 @@
     #[test]
     // this test point locks down query beyond the end of boolean section
     fn test_boolean_out_of_range() {
-        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes();
         let error =
             find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, 8).unwrap_err();
         assert_eq!(
@@ -109,7 +111,7 @@
     #[test]
     // this test point locks down query error when file has a higher version
     fn test_higher_version_storage_file() {
-        let mut info_list = create_test_flag_info_list();
+        let mut info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
         info_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;
         let flag_info = info_list.into_bytes();
         let error = find_flag_attribute(&flag_info[..], FlagValueType::Boolean, 4).unwrap_err();
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
index e9bc604..3e87acc 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
@@ -74,12 +74,12 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::test_utils::create_test_flag_table;
+    use aconfig_storage_file::{test_utils::create_test_flag_table, DEFAULT_FILE_VERSION};
 
     #[test]
     // this test point locks down table query
     fn test_flag_query() {
-        let flag_table = create_test_flag_table().into_bytes();
+        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes();
         let baseline = vec![
             (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
             (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
@@ -101,7 +101,7 @@
     #[test]
     // this test point locks down table query of a non exist flag
     fn test_not_existed_flag_query() {
-        let flag_table = create_test_flag_table().into_bytes();
+        let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes();
         let flag_context = find_flag_read_context(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
         assert_eq!(flag_context, None);
         let flag_context = find_flag_read_context(&flag_table[..], 2, "disabled_rw").unwrap();
@@ -111,7 +111,7 @@
     #[test]
     // this test point locks down query error when file has a higher version
     fn test_higher_version_storage_file() {
-        let mut table = create_test_flag_table();
+        let mut table = create_test_flag_table(DEFAULT_FILE_VERSION);
         table.header.version = MAX_SUPPORTED_FILE_VERSION + 1;
         let flag_table = table.into_bytes();
         let error = find_flag_read_context(&flag_table[..], 0, "enabled_ro").unwrap_err();
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
index 12c1e83..35f5692 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
@@ -48,12 +48,12 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::test_utils::create_test_flag_value_list;
+    use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION};
 
     #[test]
     // this test point locks down flag value query
     fn test_flag_value_query() {
-        let flag_value_list = create_test_flag_value_list().into_bytes();
+        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();
         let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];
         for (offset, expected_value) in baseline.into_iter().enumerate() {
             let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
@@ -64,7 +64,7 @@
     #[test]
     // this test point locks down query beyond the end of boolean section
     fn test_boolean_out_of_range() {
-        let flag_value_list = create_test_flag_value_list().into_bytes();
+        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();
         let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
@@ -75,7 +75,7 @@
     #[test]
     // this test point locks down query error when file has a higher version
     fn test_higher_version_storage_file() {
-        let mut value_list = create_test_flag_value_list();
+        let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
         value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;
         let flag_value = value_list.into_bytes();
         let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index 884f148..6e98fe9 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -44,6 +44,7 @@
 
 pub use aconfig_storage_file::{AconfigStorageError, FlagValueType, StorageFileType};
 pub use flag_table_query::FlagReadContext;
+pub use mapped_file::map_file;
 pub use package_table_query::PackageReadContext;
 
 use aconfig_storage_file::read_u32_from_bytes;
@@ -114,13 +115,13 @@
 
 /// Get the boolean flag value.
 ///
-/// \input file: mapped flag file
+/// \input file: a byte slice, can be either &Mmap or &MapMut
 /// \input index: boolean flag offset
 ///
 /// \return
 /// If the provide offset is valid, it returns the boolean flag value, otherwise it
 /// returns the error message.
-pub fn get_boolean_flag_value(file: &Mmap, index: u32) -> Result<bool, AconfigStorageError> {
+pub fn get_boolean_flag_value(file: &[u8], index: u32) -> Result<bool, AconfigStorageError> {
     find_boolean_flag_value(file, index)
 }
 
@@ -148,7 +149,7 @@
 
 /// Get the flag attribute.
 ///
-/// \input file: mapped flag info file
+/// \input file: a byte slice, can be either &Mmap or &MapMut
 /// \input flag_type: flag value type
 /// \input flag_index: flag index
 ///
@@ -156,7 +157,7 @@
 /// If the provide offset is valid, it returns the flag attribute bitfiled, otherwise it
 /// returns the error message.
 pub fn get_flag_attribute(
-    file: &Mmap,
+    file: &[u8],
     flag_type: FlagValueType,
     flag_index: u32,
 ) -> Result<u8, AconfigStorageError> {
@@ -412,10 +413,10 @@
         let flag_map = storage_dir.clone() + "/maps/mockup.flag.map";
         let flag_val = storage_dir.clone() + "/boot/mockup.val";
         let flag_info = storage_dir.clone() + "/boot/mockup.info";
-        fs::copy("./tests/data/v1/package.map", &package_map).unwrap();
-        fs::copy("./tests/data/v1/flag.map", &flag_map).unwrap();
-        fs::copy("./tests/data/v1/flag.val", &flag_val).unwrap();
-        fs::copy("./tests/data/v1/flag.info", &flag_info).unwrap();
+        fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap();
 
         return storage_dir;
     }
@@ -507,9 +508,9 @@
     #[test]
     // this test point locks down flag storage file version number query api
     fn test_storage_version_query() {
-        assert_eq!(get_storage_file_version("./tests/data/v1/package.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./tests/data/v1/flag.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./tests/data/v1/flag.val").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./tests/data/v1/flag.info").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/data/v1/package_v1.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.val").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.info").unwrap(), 1);
     }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
index 32dbed8..f4e269e 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -28,7 +28,7 @@
 /// The memory mapped file may have undefined behavior if there are writes to this
 /// file after being mapped. Ensure no writes can happen to this file while this
 /// mapping stays alive.
-unsafe fn map_file(file_path: &str) -> Result<Mmap, AconfigStorageError> {
+pub unsafe fn map_file(file_path: &str) -> Result<Mmap, AconfigStorageError> {
     let file = File::open(file_path)
         .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
     unsafe {
@@ -97,10 +97,10 @@
         let flag_map = storage_dir.clone() + "/maps/mockup.flag.map";
         let flag_val = storage_dir.clone() + "/boot/mockup.val";
         let flag_info = storage_dir.clone() + "/boot/mockup.info";
-        fs::copy("./tests/data/v1/package.map", &package_map).unwrap();
-        fs::copy("./tests/data/v1/flag.map", &flag_map).unwrap();
-        fs::copy("./tests/data/v1/flag.val", &flag_val).unwrap();
-        fs::copy("./tests/data/v1/flag.info", &flag_info).unwrap();
+        fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap();
+        fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap();
 
         return storage_dir;
     }
@@ -108,9 +108,9 @@
     #[test]
     fn test_mapped_file_contents() {
         let storage_dir = create_test_storage_files();
-        map_and_verify(&storage_dir, StorageFileType::PackageMap, "./tests/data/v1/package.map");
-        map_and_verify(&storage_dir, StorageFileType::FlagMap, "./tests/data/v1/flag.map");
-        map_and_verify(&storage_dir, StorageFileType::FlagVal, "./tests/data/v1/flag.val");
-        map_and_verify(&storage_dir, StorageFileType::FlagInfo, "./tests/data/v1/flag.info");
+        map_and_verify(&storage_dir, StorageFileType::PackageMap, "./tests/data/v1/package_v1.map");
+        map_and_verify(&storage_dir, StorageFileType::FlagMap, "./tests/data/v1/flag_v1.map");
+        map_and_verify(&storage_dir, StorageFileType::FlagVal, "./tests/data/v1/flag_v1.val");
+        map_and_verify(&storage_dir, StorageFileType::FlagInfo, "./tests/data/v1/flag_v1.info");
     }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
index acb60f6..a4b63ab 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
@@ -74,12 +74,12 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::test_utils::create_test_package_table;
+    use aconfig_storage_file::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION};
 
     #[test]
     // this test point locks down table query
     fn test_package_query() {
-        let package_table = create_test_package_table().into_bytes();
+        let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes();
         let package_context =
             find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
                 .unwrap()
@@ -104,7 +104,7 @@
     // this test point locks down table query of a non exist package
     fn test_not_existed_package_query() {
         // this will land at an empty bucket
-        let package_table = create_test_package_table().into_bytes();
+        let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes();
         let package_context =
             find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_3")
                 .unwrap();
@@ -119,7 +119,7 @@
     #[test]
     // this test point locks down query error when file has a higher version
     fn test_higher_version_storage_file() {
-        let mut table = create_test_package_table();
+        let mut table = create_test_package_table(DEFAULT_FILE_VERSION);
         table.header.version = MAX_SUPPORTED_FILE_VERSION + 1;
         let package_table = table.into_bytes();
         let error =
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java
new file mode 100644
index 0000000..c2bef31
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import android.os.StrictMode;
+
+import java.nio.file.Path;
+
+/** @hide */
+public class AconfigPackageImpl {
+    private FlagTable mFlagTable;
+    private FlagValueList mFlagValueList;
+    private PackageTable.Node mPNode;
+
+    /** @hide */
+    private AconfigPackageImpl() {}
+
+    /** @hide */
+    public static AconfigPackageImpl load(String packageName, StorageFileProvider fileProvider) {
+        AconfigPackageImpl impl = new AconfigPackageImpl();
+        impl.init(null, packageName, fileProvider);
+        return impl;
+    }
+
+    /** @hide */
+    public static AconfigPackageImpl load(
+            String container, String packageName, StorageFileProvider fileProvider) {
+        if (container == null) {
+            throw new AconfigStorageException(
+                    AconfigStorageException.ERROR_CONTAINER_NOT_FOUND,
+                    "container null cannot be found on the device");
+        }
+        AconfigPackageImpl impl = new AconfigPackageImpl();
+        impl.init(container, packageName, fileProvider);
+        return impl;
+    }
+
+    /** @hide */
+    public boolean getBooleanFlagValue(String flagName, boolean defaultValue) {
+        FlagTable.Node fNode = mFlagTable.get(mPNode.getPackageId(), flagName);
+        // no such flag in this package
+        if (fNode == null) return defaultValue;
+        int index = fNode.getFlagIndex() + mPNode.getBooleanStartIndex();
+        return mFlagValueList.getBoolean(index);
+    }
+
+    /** @hide */
+    public boolean getBooleanFlagValue(int index) {
+        return mFlagValueList.getBoolean(index + mPNode.getBooleanStartIndex());
+    }
+
+    /** @hide */
+    public long getPackageFingerprint() {
+        return mPNode.getPackageFingerprint();
+    }
+
+    /** @hide */
+    public boolean hasPackageFingerprint() {
+        return mPNode.hasPackageFingerprint();
+    }
+
+    private void init(String containerName, String packageName, StorageFileProvider fileProvider) {
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        String container = containerName;
+        try {
+            // for devices don't have new storage directly return
+            if (!fileProvider.containerFileExists(null)) {
+                throw new AconfigStorageException(
+                        AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND,
+                        "aconfig storage cannot be found on the device");
+            }
+            PackageTable.Node pNode = null;
+
+            if (container == null) {
+                // Check if the device has flag files on the system partition.
+                // If the device does, search the system partition first.
+                container = "system";
+                if (fileProvider.containerFileExists(container)) {
+                    pNode = fileProvider.getPackageTable(container).get(packageName);
+                }
+
+                if (pNode == null) {
+                    // Search all package map files if not found in the system partition.
+                    for (Path p : fileProvider.listPackageMapFiles()) {
+                        PackageTable pTable = StorageFileProvider.getPackageTable(p);
+                        pNode = pTable.get(packageName);
+                        if (pNode != null) {
+                            container = pTable.getHeader().getContainer();
+                            break;
+                        }
+                    }
+                }
+            } else {
+                if (!fileProvider.containerFileExists(container)) {
+                    throw new AconfigStorageException(
+                            AconfigStorageException.ERROR_CONTAINER_NOT_FOUND,
+                            "container " + container + " cannot be found on the device");
+                }
+                pNode = fileProvider.getPackageTable(container).get(packageName);
+            }
+
+            if (pNode == null) {
+                // for the case package is not found in all container, return instead of throwing
+                // error
+                throw new AconfigStorageException(
+                        AconfigStorageException.ERROR_PACKAGE_NOT_FOUND,
+                        "package "
+                                + packageName
+                                + " in container "
+                                + container
+                                + " cannot be found on the device");
+            }
+
+            mFlagTable = fileProvider.getFlagTable(container);
+            mFlagValueList = fileProvider.getFlagValueList(container);
+            mPNode = pNode;
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java
new file mode 100644
index 0000000..b28341e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.io.Closeable;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+public class StorageFileProvider {
+
+    private static final String DEFAULT_MAP_PATH = "/metadata/aconfig/maps/";
+    private static final String DEFAULT_BOOT_PATH = "/metadata/aconfig/boot/";
+    private static final String PMAP_FILE_EXT = ".package.map";
+    private static final String FMAP_FILE_EXT = ".flag.map";
+    private static final String VAL_FILE_EXT = ".val";
+
+    private final String mMapPath;
+    private final String mBootPath;
+
+    /** @hide */
+    public static StorageFileProvider getDefaultProvider() {
+        return new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH);
+    }
+
+    /** @hide */
+    public StorageFileProvider(String mapPath, String bootPath) {
+        mMapPath = mapPath;
+        mBootPath = bootPath;
+    }
+
+    /** @hide */
+    public boolean containerFileExists(String container) {
+        if (container == null) {
+            return Files.exists(Paths.get(mMapPath));
+        }
+        return Files.exists(Paths.get(mMapPath, container + PMAP_FILE_EXT));
+    }
+
+    /** @hide */
+    public List<Path> listPackageMapFiles() {
+        List<Path> result = new ArrayList<>();
+        try {
+            DirectoryStream<Path> stream =
+                    Files.newDirectoryStream(Paths.get(mMapPath), "*" + PMAP_FILE_EXT);
+            for (Path entry : stream) {
+                result.add(entry);
+                // sb.append(entry. toString());
+            }
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    String.format("Fail to list map files in path %s", mMapPath), e);
+        }
+
+        return result;
+    }
+
+    /** @hide */
+    public PackageTable getPackageTable(String container) {
+        return getPackageTable(Paths.get(mMapPath, container + PMAP_FILE_EXT));
+    }
+
+    /** @hide */
+    public FlagTable getFlagTable(String container) {
+        return FlagTable.fromBytes(mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT)));
+    }
+
+    /** @hide */
+    public FlagValueList getFlagValueList(String container) {
+        return FlagValueList.fromBytes(
+                mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT)));
+    }
+
+    /** @hide */
+    public static PackageTable getPackageTable(Path path) {
+        return PackageTable.fromBytes(mapStorageFile(path));
+    }
+
+    // Map a storage file given file path
+    private static MappedByteBuffer mapStorageFile(Path file) {
+        FileChannel channel = null;
+        try {
+            channel = FileChannel.open(file, StandardOpenOption.READ);
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE,
+                    String.format("Fail to mmap storage file %s", file),
+                    e);
+        } finally {
+            quietlyDispose(channel);
+        }
+    }
+
+    private static void quietlyDispose(Closeable closable) {
+        try {
+            if (closable != null) {
+                closable.close();
+            }
+        } catch (Exception e) {
+            // no need to care, at least as of now
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index b8e510d..6b8942b 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -1,10 +1,14 @@
 filegroup {
     name: "read_api_test_storage_files",
     srcs: [
-        "data/v1/package.map",
-        "data/v1/flag.map",
-        "data/v1/flag.val",
-        "data/v1/flag.info",
+        "data/v1/package_v1.map",
+        "data/v1/flag_v1.map",
+        "data/v1/flag_v1.val",
+        "data/v1/flag_v1.info",
+        "data/v2/package_v2.map",
+        "data/v2/flag_v2.map",
+        "data/v2/flag_v2.val",
+        "data/v2/flag_v2.info",
     ],
 }
 
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.info
similarity index 100%
rename from tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.info
rename to tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.map
similarity index 100%
rename from tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.map
rename to tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.val b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.val
similarity index 100%
rename from tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.val
rename to tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package_v1.map
similarity index 100%
rename from tools/aconfig/aconfig_storage_read_api/tests/data/v1/package.map
rename to tools/aconfig/aconfig_storage_read_api/tests/data/v1/package_v1.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info
new file mode 100644
index 0000000..9db7fde
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map
new file mode 100644
index 0000000..cf4685c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val
new file mode 100644
index 0000000..37d4750
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map
new file mode 100644
index 0000000..16f4054
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
new file mode 100644
index 0000000..18f70e8
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.AconfigPackageImpl;
+import android.aconfig.storage.AconfigStorageException;
+import android.aconfig.storage.StorageFileProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AconfigPackageImplTest {
+
+    private StorageFileProvider pr;
+
+    @Before
+    public void setup() {
+        pr = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+    }
+
+    @Test
+    public void testLoad_onlyPackageName() throws Exception {
+        AconfigPackageImpl p = AconfigPackageImpl.load("com.android.aconfig.storage.test_1", pr);
+        assertNotNull(p);
+    }
+
+    @Test
+    public void testLoad_groupNameFingerprint() throws Exception {
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertNotNull(p);
+    }
+
+    @Test
+    public void testLoad_error() throws Exception {
+        AconfigPackageImpl p;
+        AconfigStorageException e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "mockup", "com.android.aconfig.storage.test_10", pr));
+        // cannot find package
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
+        // cannot find package
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () -> AconfigPackageImpl.load("com.android.aconfig.storage.test_10", pr));
+        assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode());
+        // cannot find container
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        null, "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode());
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "test", "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode());
+
+        // new storage doesn't exist
+        pr = new StorageFileProvider("fake/path/", "fake/path/");
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () -> AconfigPackageImpl.load("fake_package", pr));
+        assertEquals(AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND, e.getErrorCode());
+
+        // file read issue
+        pr = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, "fake/path/");
+        e =
+                assertThrows(
+                        AconfigStorageException.class,
+                        () ->
+                                AconfigPackageImpl.load(
+                                        "mockup", "com.android.aconfig.storage.test_1", pr));
+        assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode());
+    }
+
+    @Test
+    public void testGetBooleanFlagValue_flagName() throws Exception {
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.getBooleanFlagValue("disabled_rw", true));
+        assertTrue(p.getBooleanFlagValue("enabled_ro", false));
+        assertTrue(p.getBooleanFlagValue("enabled_rw", false));
+        assertFalse(p.getBooleanFlagValue("fake", false));
+    }
+
+    @Test
+    public void testGetBooleanFlagValue_index() throws Exception {
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.getBooleanFlagValue(0));
+        assertTrue(p.getBooleanFlagValue(1));
+        assertTrue(p.getBooleanFlagValue(2));
+    }
+
+    @Test
+    public void testHasPackageFingerprint() throws Exception {
+        AconfigPackageImpl p =
+                AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr);
+        assertFalse(p.hasPackageFingerprint());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
index 3d4e9ad..9c88122 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
@@ -22,3 +22,28 @@
     ],
     team: "trendy_team_android_core_experiments",
 }
+
+android_test {
+    name: "aconfig_storage_package",
+    team: "trendy_team_android_core_experiments",
+    srcs: [
+        "AconfigPackageImplTest.java",
+        "StorageFileProviderTest.java",
+        "TestDataUtils.java",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "junit",
+        "aconfig_storage_reader_java",
+    ],
+    test_config: "AndroidStorageJaveTest.xml",
+    manifest: "AndroidPackageTestManifest.xml",
+    sdk_version: "test_current",
+    data: [
+        ":read_api_test_storage_files",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+    jarjar_rules: "jarjar.txt",
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml
new file mode 100644
index 0000000..5e01879
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.aconfig.storage.test">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.aconfig.storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml
new file mode 100644
index 0000000..861b9b5
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Test aconfig storage java tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="aconfig_storage_package.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="package_v1.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.package.map" />
+        <option name="push" value="flag_v1.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.flag.map" />
+        <option name="push" value="flag_v1.val->/data/local/tmp/aconfig_storage_package/testdata/mockup.val" />
+        <option name="push" value="flag_v1.info->/data/local/tmp/aconfig_storage_package/testdata/mockup.info" />
+        <option name="post-push" value="chmod +r /data/local/tmp/aconfig_storage_package/testdata/" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.aconfig.storage.test" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml
index 99c9e25..7ffa18c 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml
@@ -35,11 +35,11 @@
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="abort-on-push-failure" value="true" />
-        <option name="push-file" key="package.map"
+        <option name="push-file" key="package_v1.map"
                 value="/data/local/tmp/aconfig_java_api_test/maps/mockup.package.map" />
-        <option name="push-file" key="flag.map"
+        <option name="push-file" key="flag_v1.map"
                 value="/data/local/tmp/aconfig_java_api_test/maps/mockup.flag.map" />
-        <option name="push-file" key="flag.val"
+        <option name="push-file" key="flag_v1.val"
                 value="/data/local/tmp/aconfig_java_api_test/boot/mockup.val" />
     </target_preparer>
 
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java
new file mode 100644
index 0000000..ba1ae9e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+import android.aconfig.storage.StorageFileProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class StorageFileProviderTest {
+
+    @Test
+    public void testContainerFileExists() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        assertTrue(p.containerFileExists(null));
+        assertTrue(p.containerFileExists("mockup"));
+        assertFalse(p.containerFileExists("fake"));
+    }
+
+    @Test
+    public void testListpackageMapFiles() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        // throw new Exception(Environment.getExternalStorageDirectory().getAbsolutePath());
+        List<Path> file = p.listPackageMapFiles();
+        assertEquals(1, file.size());
+        assertTrue(
+                file.get(0)
+                        .equals(
+                                Paths.get(
+                                        TestDataUtils.TESTDATA_PATH,
+                                        TestDataUtils.TEST_PACKAGE_MAP_PATH)));
+    }
+
+    @Test
+    public void testLoadFiles() throws Exception {
+        StorageFileProvider p =
+                new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH);
+        PackageTable pt = p.getPackageTable("mockup");
+        assertNotNull(pt);
+        pt =
+                StorageFileProvider.getPackageTable(
+                        Paths.get(
+                                TestDataUtils.TESTDATA_PATH, TestDataUtils.TEST_PACKAGE_MAP_PATH));
+        assertNotNull(pt);
+        FlagTable f = p.getFlagTable("mockup");
+        assertNotNull(f);
+        FlagValueList v = p.getFlagValueList("mockup");
+        assertNotNull(v);
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java b/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java
new file mode 100644
index 0000000..d5cddc7
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public final class TestDataUtils {
+    public static final String TEST_PACKAGE_MAP_PATH = "mockup.package.map";
+    public static final String TEST_FLAG_MAP_PATH = "mockup.flag.map";
+    public static final String TEST_FLAG_VAL_PATH = "mockup.val";
+    public static final String TEST_FLAG_INFO_PATH = "mockup.info";
+
+    public static final String TESTDATA_PATH =
+            "/data/local/tmp/aconfig_storage_package/testdata/";
+
+    public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
+    }
+
+    public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
+    }
+
+    private static ByteBuffer readFile(String fileName) throws Exception {
+        InputStream input = new FileInputStream(fileName);
+        return ByteBuffer.wrap(input.readAllBytes());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt
new file mode 100644
index 0000000..24952ec
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt
@@ -0,0 +1,17 @@
+rule android.aconfig.storage.AconfigStorageException android.aconfig.storage.test.AconfigStorageException
+rule android.aconfig.storage.FlagTable android.aconfig.storage.test.FlagTable
+rule android.aconfig.storage.PackageTable android.aconfig.storage.test.PackageTable
+rule android.aconfig.storage.ByteBufferReader android.aconfig.storage.test.ByteBufferReader
+rule android.aconfig.storage.FlagType android.aconfig.storage.test.FlagType
+rule android.aconfig.storage.SipHasher13 android.aconfig.storage.test.SipHasher13
+rule android.aconfig.storage.FileType android.aconfig.storage.test.FileType
+rule android.aconfig.storage.FlagValueList android.aconfig.storage.test.FlagValueList
+rule android.aconfig.storage.TableUtils android.aconfig.storage.test.TableUtils
+rule android.aconfig.storage.AconfigPackageImpl android.aconfig.storage.test.AconfigPackageImpl
+rule android.aconfig.storage.StorageFileProvider android.aconfig.storage.test.StorageFileProvider
+
+
+rule android.aconfig.storage.FlagTable$* android.aconfig.storage.test.FlagTable$@1
+rule android.aconfig.storage.PackageTable$* android.aconfig.storage.test.PackageTable$@1
+rule android.aconfig.storage.FlagValueList$* android.aconfig.storage.test.FlagValueList@1
+rule android.aconfig.storage.SipHasher13$* android.aconfig.storage.test.SipHasher13@1
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
index 7537643..5289faa 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
@@ -56,10 +56,10 @@
     flag_map = std::string(maps_dir) + "/mockup.flag.map";
     flag_val = std::string(boot_dir) + "/mockup.val";
     flag_info = std::string(boot_dir) + "/mockup.info";
-    copy_file(test_dir + "/package.map", package_map);
-    copy_file(test_dir + "/flag.map", flag_map);
-    copy_file(test_dir + "/flag.val", flag_val);
-    copy_file(test_dir + "/flag.info", flag_info);
+    copy_file(test_dir + "/package_v1.map", package_map);
+    copy_file(test_dir + "/flag_v1.map", flag_map);
+    copy_file(test_dir + "/flag_v1.val", flag_val);
+    copy_file(test_dir + "/flag_v1.info", flag_info);
   }
 
   void TearDown() override {
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index 0d943f8..5605a41 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -9,7 +9,7 @@
     use rand::Rng;
     use std::fs;
 
-    fn create_test_storage_files() -> String {
+    fn create_test_storage_files(version: u32) -> String {
         let mut rng = rand::thread_rng();
         let number: u32 = rng.gen();
         let storage_dir = String::from("/tmp/") + &number.to_string();
@@ -26,17 +26,17 @@
         let flag_map = storage_dir.clone() + "/maps/mockup.flag.map";
         let flag_val = storage_dir.clone() + "/boot/mockup.val";
         let flag_info = storage_dir.clone() + "/boot/mockup.info";
-        fs::copy("./data/v1/package.map", package_map).unwrap();
-        fs::copy("./data/v1/flag.map", flag_map).unwrap();
-        fs::copy("./data/v1/flag.val", flag_val).unwrap();
-        fs::copy("./data/v1/flag.info", flag_info).unwrap();
+        fs::copy(format!("./data/v{0}/package_v{0}.map", version), package_map).unwrap();
+        fs::copy(format!("./data/v{0}/flag_v{0}.map", version), flag_map).unwrap();
+        fs::copy(format!("./data/v{}/flag_v{0}.val", version), flag_val).unwrap();
+        fs::copy(format!("./data/v{}/flag_v{0}.info", version), flag_info).unwrap();
 
         storage_dir
     }
 
     #[test]
-    fn test_unavailable_stoarge() {
-        let storage_dir = create_test_storage_files();
+    fn test_unavailable_storage() {
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let err = unsafe {
@@ -53,7 +53,7 @@
 
     #[test]
     fn test_package_context_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let package_mapped_file = unsafe {
@@ -84,7 +84,7 @@
 
     #[test]
     fn test_none_exist_package_context_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let package_mapped_file = unsafe {
@@ -99,7 +99,7 @@
 
     #[test]
     fn test_flag_context_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_mapped_file =
@@ -125,7 +125,7 @@
 
     #[test]
     fn test_none_exist_flag_context_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_mapped_file =
@@ -141,7 +141,7 @@
 
     #[test]
     fn test_boolean_flag_value_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_value_file =
@@ -155,7 +155,7 @@
 
     #[test]
     fn test_invalid_boolean_flag_value_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_value_file =
@@ -169,7 +169,7 @@
 
     #[test]
     fn test_flag_info_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_info_file =
@@ -186,7 +186,7 @@
 
     #[test]
     fn test_invalid_boolean_flag_info_query() {
-        let storage_dir = create_test_storage_files();
+        let storage_dir = create_test_storage_files(1);
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_info_file =
@@ -199,10 +199,18 @@
     }
 
     #[test]
-    fn test_storage_version_query() {
-        assert_eq!(get_storage_file_version("./data/v1/package.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./data/v1/flag.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./data/v1/flag.val").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./data/v1/flag.info").unwrap(), 1);
+    fn test_storage_version_query_v1() {
+        assert_eq!(get_storage_file_version("./data/v1/package_v1.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./data/v1/flag_v1.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./data/v1/flag_v1.val").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./data/v1/flag_v1.info").unwrap(), 1);
+    }
+
+    #[test]
+    fn test_storage_version_query_v2() {
+        assert_eq!(get_storage_file_version("./data/v2/package_v2.map").unwrap(), 2);
+        assert_eq!(get_storage_file_version("./data/v2/flag_v2.map").unwrap(), 2);
+        assert_eq!(get_storage_file_version("./data/v2/flag_v2.val").unwrap(), 2);
+        assert_eq!(get_storage_file_version("./data/v2/flag_v2.info").unwrap(), 2);
     }
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/Android.bp b/tools/aconfig/aconfig_storage_write_api/Android.bp
index 0f1962c..4c882b4 100644
--- a/tools/aconfig/aconfig_storage_write_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/Android.bp
@@ -16,6 +16,11 @@
         "libaconfig_storage_file",
         "libaconfig_storage_read_api",
     ],
+    min_sdk_version: "34",
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 rust_library {
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
index 5640922..5721105 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
@@ -94,13 +94,13 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::test_utils::create_test_flag_info_list;
+    use aconfig_storage_file::{test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION};
     use aconfig_storage_read_api::flag_info_query::find_flag_attribute;
 
     #[test]
     // this test point locks down has server override update
     fn test_update_flag_has_server_override() {
-        let flag_info_list = create_test_flag_info_list();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
         let mut buf = flag_info_list.into_bytes();
         for i in 0..flag_info_list.header.num_flags {
             update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();
@@ -115,7 +115,7 @@
     #[test]
     // this test point locks down has local override update
     fn test_update_flag_has_local_override() {
-        let flag_info_list = create_test_flag_info_list();
+        let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION);
         let mut buf = flag_info_list.into_bytes();
         for i in 0..flag_info_list.header.num_flags {
             update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
index 06a9b15..9772db9 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
@@ -49,12 +49,12 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::test_utils::create_test_flag_value_list;
+    use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION};
 
     #[test]
     // this test point locks down flag value update
     fn test_boolean_flag_value_update() {
-        let flag_value_list = create_test_flag_value_list();
+        let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
         let value_offset = flag_value_list.header.boolean_value_offset;
         let mut content = flag_value_list.into_bytes();
         let true_byte = u8::from(true).to_le_bytes()[0];
@@ -72,7 +72,7 @@
     #[test]
     // this test point locks down update beyond the end of boolean section
     fn test_boolean_out_of_range() {
-        let mut flag_value_list = create_test_flag_value_list().into_bytes();
+        let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes();
         let error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
@@ -83,7 +83,7 @@
     #[test]
     // this test point locks down query error when file has a higher version
     fn test_higher_version_storage_file() {
-        let mut value_list = create_test_flag_value_list();
+        let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION);
         value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1;
         let mut flag_value = value_list.into_bytes();
         let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err();
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
index 68edf7d..3f593fe 100644
--- a/tools/aconfig/aflags/src/aconfig_storage_source.rs
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -93,7 +93,12 @@
         special_fields: SpecialFields::new(),
     };
 
-    let mut socket = UnixStream::connect("/dev/socket/aconfigd")?;
+    let socket_name = if aconfig_flags::auto_generated::enable_system_aconfigd_rust() {
+        "/dev/socket/aconfigd_system"
+    } else {
+        "/dev/socket/aconfigd"
+    };
+    let mut socket = UnixStream::connect(socket_name)?;
 
     let message_buffer = messages.write_to_bytes()?;
     let mut message_length_buffer: [u8; 4] = [0; 4];
diff --git a/tools/aconfig/aflags/src/load_protos.rs b/tools/aconfig/aflags/src/load_protos.rs
index 90d8599..c5ac8ff 100644
--- a/tools/aconfig/aflags/src/load_protos.rs
+++ b/tools/aconfig/aflags/src/load_protos.rs
@@ -51,7 +51,10 @@
 
     let paths = aconfig_device_paths::parsed_flags_proto_paths()?;
     for path in paths {
-        let bytes = fs::read(path.clone())?;
+        let Ok(bytes) = fs::read(&path) else {
+            eprintln!("warning: failed to read {:?}", path);
+            continue;
+        };
         let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?;
         for flag in parsed_flags.parsed_flag {
             // TODO(b/334954748): enforce one-container-per-flag invariant.
@@ -60,3 +63,10 @@
     }
     Ok(result)
 }
+
+pub(crate) fn list_containers() -> Result<Vec<String>> {
+    Ok(aconfig_device_paths::parsed_flags_proto_paths()?
+        .into_iter()
+        .map(|p| infer_container(&p))
+        .collect())
+}
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 07b7243..8173bc2 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -253,6 +253,14 @@
         FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
         FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
     };
+
+    if let Some(ref c) = container {
+        ensure!(
+            load_protos::list_containers()?.contains(c),
+            format!("container '{}' not found", &c)
+        );
+    }
+
     let flags = (Filter { container }).apply(&flags_unfiltered);
     let padding_info = PaddingInfo {
         longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
@@ -298,7 +306,7 @@
         Command::List { container } => {
             if aconfig_flags::auto_generated::enable_only_new_storage() {
                 list(FlagSourceType::AconfigStorage, container)
-                    .map_err(|err| anyhow!("storage may not be enabled: {err}"))
+                    .map_err(|err| anyhow!("could not list flags: {err}"))
                     .map(Some)
             } else {
                 list(FlagSourceType::DeviceConfig, container).map(Some)
diff --git a/tools/aconfig/fake_device_config/src/android/os/Binder.java b/tools/aconfig/fake_device_config/src/android/os/Binder.java
new file mode 100644
index 0000000..8a2313d
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/os/Binder.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+public class Binder {
+    public static final long clearCallingIdentity() {
+        throw new UnsupportedOperationException("Stub!");
+    }
+    public static final void restoreCallingIdentity(long token) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+}
diff --git a/tools/edit_monitor/Android.bp b/tools/edit_monitor/Android.bp
index 3497821..b8ac5bf 100644
--- a/tools/edit_monitor/Android.bp
+++ b/tools/edit_monitor/Android.bp
@@ -35,6 +35,13 @@
     pkg_path: "edit_monitor",
     srcs: [
         "daemon_manager.py",
+        "edit_monitor.py",
+        "utils.py",
+    ],
+    libs: [
+        "asuite_cc_client",
+        "edit_event_proto",
+        "watchdog",
     ],
 }
 
@@ -53,6 +60,51 @@
     },
 }
 
+python_test_host {
+    name: "edit_monitor_test",
+    main: "edit_monitor_test.py",
+    pkg_path: "edit_monitor",
+    srcs: [
+        "edit_monitor_test.py",
+    ],
+    libs: [
+        "edit_monitor_lib",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+python_test_host {
+    name: "edit_monitor_utils_test",
+    main: "utils_test.py",
+    pkg_path: "edit_monitor",
+    srcs: [
+        "utils_test.py",
+    ],
+    libs: [
+        "edit_monitor_lib",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+python_test_host {
+    name: "edit_monitor_integration_test",
+    main: "edit_monitor_integration_test.py",
+    pkg_path: "testdata",
+    srcs: [
+        "edit_monitor_integration_test.py",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    data: [
+        ":edit_monitor",
+    ],
+}
+
 python_binary_host {
     name: "edit_monitor",
     pkg_path: "edit_monitor",
diff --git a/tools/edit_monitor/daemon_manager.py b/tools/edit_monitor/daemon_manager.py
index 892c292..afdefc5 100644
--- a/tools/edit_monitor/daemon_manager.py
+++ b/tools/edit_monitor/daemon_manager.py
@@ -13,24 +13,33 @@
 # limitations under the License.
 
 
+import getpass
 import hashlib
 import logging
 import multiprocessing
 import os
 import pathlib
+import platform
 import signal
 import subprocess
 import sys
 import tempfile
 import time
 
+from atest.metrics import clearcut_client
+from atest.proto import clientanalytics_pb2
+from edit_monitor import utils
+from proto import edit_event_pb2
 
-DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 1
+DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 5
 DEFAULT_MONITOR_INTERVAL_SECONDS = 5
-DEFAULT_MEMORY_USAGE_THRESHOLD = 2000
+DEFAULT_MEMORY_USAGE_THRESHOLD = 2 * 1024  # 2GB
 DEFAULT_CPU_USAGE_THRESHOLD = 200
 DEFAULT_REBOOT_TIMEOUT_SECONDS = 60 * 60 * 24
 BLOCK_SIGN_FILE = "edit_monitor_block_sign"
+# Enum of the Clearcut log source defined under
+# /google3/wireless/android/play/playlog/proto/log_source_enum.proto
+LOG_SOURCE = 2524
 
 
 def default_daemon_target():
@@ -46,11 +55,16 @@
       binary_path: str,
       daemon_target: callable = default_daemon_target,
       daemon_args: tuple = (),
+      cclient: clearcut_client.Clearcut | None = None,
   ):
     self.binary_path = binary_path
     self.daemon_target = daemon_target
     self.daemon_args = daemon_args
+    self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE)
 
+    self.user_name = getpass.getuser()
+    self.host_name = platform.node()
+    self.source_root = os.environ.get("ANDROID_BUILD_TOP", "")
     self.pid = os.getpid()
     self.daemon_process = None
 
@@ -66,17 +80,33 @@
 
   def start(self):
     """Writes the pidfile and starts the daemon proces."""
+    if not utils.is_feature_enabled(
+        "edit_monitor",
+        self.user_name,
+        "ENABLE_ANDROID_EDIT_MONITOR",
+        10,
+    ):
+      logging.warning("Edit monitor is disabled, exiting...")
+      return
+
     if self.block_sign.exists():
       logging.warning("Block sign found, exiting...")
       return
 
-    if self.binary_path.startswith('/google/cog/'):
+    if self.binary_path.startswith("/google/cog/"):
       logging.warning("Edit monitor for cog is not supported, exiting...")
       return
 
-    self._stop_any_existing_instance()
-    self._write_pid_to_pidfile()
-    self._start_daemon_process()
+    try:
+      self._stop_any_existing_instance()
+      self._write_pid_to_pidfile()
+      self._start_daemon_process()
+    except Exception as e:
+      logging.exception("Failed to start daemon manager with error %s", e)
+      self._send_error_event_to_clearcut(
+          edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
+      )
+      raise e
 
   def monitor_daemon(
       self,
@@ -118,6 +148,9 @@
         logging.error(
             "Daemon process is consuming too much resource, killing..."
         ),
+        self._send_error_event_to_clearcut(
+            edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE
+        )
         self._terminate_process(self.daemon_process.pid)
 
     logging.info(
@@ -131,14 +164,24 @@
   def stop(self):
     """Stops the daemon process and removes the pidfile."""
 
-    logging.debug("in daemon manager cleanup.")
+    logging.info("in daemon manager cleanup.")
     try:
-      if self.daemon_process and self.daemon_process.is_alive():
-        self._terminate_process(self.daemon_process.pid)
+      if self.daemon_process:
+        # The daemon process might already in termination process,
+        # wait some time before kill it explicitly.
+        self._wait_for_process_terminate(self.daemon_process.pid, 1)
+        if self.daemon_process.is_alive():
+          self._terminate_process(self.daemon_process.pid)
       self._remove_pidfile()
-      logging.debug("Successfully stopped daemon manager.")
+      logging.info("Successfully stopped daemon manager.")
     except Exception as e:
       logging.exception("Failed to stop daemon manager with error %s", e)
+      self._send_error_event_to_clearcut(
+          edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR
+      )
+      sys.exit(1)
+    finally:
+      self.cclient.flush_events()
 
   def reboot(self):
     """Reboots the current process.
@@ -146,7 +189,7 @@
     Stops the current daemon manager and reboots the entire process based on
     the binary file. Exits directly If the binary file no longer exists.
     """
-    logging.debug("Rebooting process based on binary %s.", self.binary_path)
+    logging.info("Rebooting process based on binary %s.", self.binary_path)
 
     # Stop the current daemon manager first.
     self.stop()
@@ -160,6 +203,9 @@
       os.execv(self.binary_path, sys.argv)
     except OSError as e:
       logging.exception("Failed to reboot process with error: %s.", e)
+      self._send_error_event_to_clearcut(
+          edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR
+      )
       sys.exit(1)  # Indicate an error occurred
 
   def cleanup(self):
@@ -171,6 +217,7 @@
     that requires immediate cleanup to prevent damanger to the system.
     """
     logging.debug("Start cleaning up all existing instances.")
+    self._send_error_event_to_clearcut(edit_event_pb2.EditEvent.FORCE_CLEANUP)
 
     try:
       # First places a block sign to prevent any edit monitor process to start.
@@ -227,6 +274,7 @@
     p = multiprocessing.Process(
         target=self.daemon_target, args=self.daemon_args
     )
+    p.daemon = True
     p.start()
 
     logging.info("Start subprocess with PID %d", p.pid)
@@ -299,36 +347,28 @@
     return pid_file_path
 
   def _get_process_memory_percent(self, pid: int) -> float:
-    try:
-      with open(f"/proc/{pid}/stat", "r") as f:
-        stat_data = f.readline().split()
-        # RSS is the 24th field in /proc/[pid]/stat
-        rss_pages = int(stat_data[23])
-        return rss_pages * 4 / 1024  # Covert to MB
-    except (FileNotFoundError, IndexError, ValueError, IOError) as e:
-      logging.exception("Failed to get memory usage.")
-      raise e
+    with open(f"/proc/{pid}/stat", "r") as f:
+      stat_data = f.readline().split()
+      # RSS is the 24th field in /proc/[pid]/stat
+      rss_pages = int(stat_data[23])
+      return rss_pages * 4 / 1024  # Covert to MB
 
   def _get_process_cpu_percent(self, pid: int, interval: int = 1) -> float:
-    try:
-      total_start_time = self._get_total_cpu_time(pid)
-      with open("/proc/uptime", "r") as f:
-        uptime_start = float(f.readline().split()[0])
+    total_start_time = self._get_total_cpu_time(pid)
+    with open("/proc/uptime", "r") as f:
+      uptime_start = float(f.readline().split()[0])
 
-      time.sleep(interval)
+    time.sleep(interval)
 
-      total_end_time = self._get_total_cpu_time(pid)
-      with open("/proc/uptime", "r") as f:
-        uptime_end = float(f.readline().split()[0])
+    total_end_time = self._get_total_cpu_time(pid)
+    with open("/proc/uptime", "r") as f:
+      uptime_end = float(f.readline().split()[0])
 
-      return (
-          (total_end_time - total_start_time)
-          / (uptime_end - uptime_start)
-          * 100
-      )
-    except (FileNotFoundError, IndexError, ValueError, IOError) as e:
-      logging.exception("Failed to get CPU usage.")
-      raise e
+    return (
+        (total_end_time - total_start_time)
+        / (uptime_end - uptime_start)
+        * 100
+    )
 
   def _get_total_cpu_time(self, pid: int) -> float:
     with open(f"/proc/{str(pid)}/stat", "r") as f:
@@ -350,4 +390,19 @@
         except (FileNotFoundError, IOError, ValueError, TypeError):
           logging.exception("Failed to get pid from file path: %s", file)
 
-    return pids
\ No newline at end of file
+    return pids
+
+  def _send_error_event_to_clearcut(self, error_type):
+    edit_monitor_error_event_proto = edit_event_pb2.EditEvent(
+        user_name=self.user_name,
+        host_name=self.host_name,
+        source_root=self.source_root,
+    )
+    edit_monitor_error_event_proto.edit_monitor_error_event.CopyFrom(
+        edit_event_pb2.EditEvent.EditMonitorErrorEvent(error_type=error_type)
+    )
+    log_event = clientanalytics_pb2.LogEvent(
+        event_time_ms=int(time.time() * 1000),
+        source_extension=edit_monitor_error_event_proto.SerializeToString(),
+    )
+    self.cclient.log(log_event)
diff --git a/tools/edit_monitor/daemon_manager_test.py b/tools/edit_monitor/daemon_manager_test.py
index 72442c6..8d18388 100644
--- a/tools/edit_monitor/daemon_manager_test.py
+++ b/tools/edit_monitor/daemon_manager_test.py
@@ -26,6 +26,7 @@
 import unittest
 from unittest import mock
 from edit_monitor import daemon_manager
+from proto import edit_event_pb2
 
 
 TEST_BINARY_FILE = '/path/to/test_binary'
@@ -80,6 +81,9 @@
     # Sets the tempdir under the working dir so any temp files created during
     # tests will be cleaned.
     tempfile.tempdir = self.working_dir.name
+    self.patch = mock.patch.dict(
+        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'})
+    self.patch.start()
 
   def tearDown(self):
     # Cleans up any child processes left by the tests.
@@ -87,6 +91,7 @@
     self.working_dir.cleanup()
     # Restores tempdir.
     tempfile.tempdir = self.original_tempdir
+    self.patch.stop()
     super().tearDown()
 
   def test_start_success_with_no_existing_instance(self):
@@ -128,13 +133,24 @@
 
     dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
     dm.start()
+
+    # Verify no daemon process is started.
+    self.assertIsNone(dm.daemon_process)
+
+  @mock.patch.dict(os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True)
+  def test_start_return_directly_if_disabled(self):
+    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
+    dm.start()
+
     # Verify no daemon process is started.
     self.assertIsNone(dm.daemon_process)
 
   def test_start_return_directly_if_in_cog_env(self):
     dm = daemon_manager.DaemonManager(
-        '/google/cog/cloud/user/workspace/edit_monitor')
+        '/google/cog/cloud/user/workspace/edit_monitor'
+    )
     dm.start()
+
     # Verify no daemon process is started.
     self.assertIsNone(dm.daemon_process)
 
@@ -148,9 +164,13 @@
     with open(pid_file_path_dir.joinpath(TEST_PID_FILE_PATH), 'w') as f:
       f.write('123456')
 
-    with self.assertRaises(OSError) as error:
-      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
+    fake_cclient = FakeClearcutClient()
+    with self.assertRaises(OSError):
+      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient)
       dm.start()
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
+    )
 
   def test_start_failed_to_write_pidfile(self):
     pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath(
@@ -160,40 +180,63 @@
     # Makes the directory read-only so write pidfile will fail.
     os.chmod(pid_file_path_dir, 0o555)
 
-    with self.assertRaises(PermissionError) as error:
-      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
+    fake_cclient = FakeClearcutClient()
+    with self.assertRaises(PermissionError):
+      dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient)
       dm.start()
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
+    )
 
   def test_start_failed_to_start_daemon_process(self):
-    with self.assertRaises(TypeError) as error:
+    fake_cclient = FakeClearcutClient()
+    with self.assertRaises(TypeError):
       dm = daemon_manager.DaemonManager(
-          TEST_BINARY_FILE, daemon_target='wrong_target', daemon_args=(1)
+          TEST_BINARY_FILE,
+          daemon_target='wrong_target',
+          daemon_args=(1),
+          cclient=fake_cclient,
       )
       dm.start()
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
+    )
 
   def test_monitor_daemon_subprocess_killed_high_memory_usage(self):
+    fake_cclient = FakeClearcutClient()
     dm = daemon_manager.DaemonManager(
         TEST_BINARY_FILE,
         daemon_target=memory_consume_daemon_target,
         daemon_args=(2,),
+        cclient=fake_cclient,
     )
     dm.start()
     dm.monitor_daemon(interval=1, memory_threshold=2)
 
     self.assertTrue(dm.max_memory_usage >= 2)
     self.assert_no_subprocess_running()
+    self._assert_error_event_logged(
+        fake_cclient,
+        edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE,
+    )
 
   def test_monitor_daemon_subprocess_killed_high_cpu_usage(self):
+    fake_cclient = FakeClearcutClient()
     dm = daemon_manager.DaemonManager(
         TEST_BINARY_FILE,
         daemon_target=cpu_consume_daemon_target,
         daemon_args=(20,),
+        cclient=fake_cclient,
     )
     dm.start()
     dm.monitor_daemon(interval=1, cpu_threshold=20)
 
     self.assertTrue(dm.max_cpu_usage >= 20)
     self.assert_no_subprocess_running()
+    self._assert_error_event_logged(
+        fake_cclient,
+        edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE,
+    )
 
   @mock.patch('subprocess.check_output')
   def test_monitor_daemon_failed_does_not_matter(self, mock_output):
@@ -207,7 +250,8 @@
     )
 
     dm = daemon_manager.DaemonManager(
-        binary_file.name, daemon_target=long_running_daemon
+        binary_file.name,
+        daemon_target=long_running_daemon,
     )
     dm.start()
     dm.monitor_daemon(reboot_timeout=0.5)
@@ -226,27 +270,42 @@
   @mock.patch('os.kill')
   def test_stop_failed_to_kill_daemon_process(self, mock_kill):
     mock_kill.side_effect = OSError('Unknown OSError')
+    fake_cclient = FakeClearcutClient()
     dm = daemon_manager.DaemonManager(
-        TEST_BINARY_FILE, daemon_target=long_running_daemon
+        TEST_BINARY_FILE,
+        daemon_target=long_running_daemon,
+        cclient=fake_cclient,
     )
-    dm.start()
-    dm.stop()
 
-    self.assertTrue(dm.daemon_process.is_alive())
-    self.assertTrue(dm.pid_file_path.exists())
+    with self.assertRaises(SystemExit):
+      dm.start()
+      dm.stop()
+      self.assertTrue(dm.daemon_process.is_alive())
+      self.assertTrue(dm.pid_file_path.exists())
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR
+    )
 
   @mock.patch('os.remove')
   def test_stop_failed_to_remove_pidfile(self, mock_remove):
     mock_remove.side_effect = OSError('Unknown OSError')
 
+    fake_cclient = FakeClearcutClient()
     dm = daemon_manager.DaemonManager(
-        TEST_BINARY_FILE, daemon_target=long_running_daemon
+        TEST_BINARY_FILE,
+        daemon_target=long_running_daemon,
+        cclient=fake_cclient,
     )
-    dm.start()
-    dm.stop()
 
-    self.assert_no_subprocess_running()
-    self.assertTrue(dm.pid_file_path.exists())
+    with self.assertRaises(SystemExit):
+      dm.start()
+      dm.stop()
+      self.assert_no_subprocess_running()
+      self.assertTrue(dm.pid_file_path.exists())
+
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR
+    )
 
   @mock.patch('os.execv')
   def test_reboot_success(self, mock_execv):
@@ -273,7 +332,7 @@
     )
     dm.start()
 
-    with self.assertRaises(SystemExit) as cm:
+    with self.assertRaises(SystemExit):
       dm.reboot()
       mock_execv.assert_not_called()
       self.assertEqual(cm.exception.code, 0)
@@ -281,18 +340,24 @@
   @mock.patch('os.execv')
   def test_reboot_failed(self, mock_execv):
     mock_execv.side_effect = OSError('Unknown OSError')
+    fake_cclient = FakeClearcutClient()
     binary_file = tempfile.NamedTemporaryFile(
         dir=self.working_dir.name, delete=False
     )
 
     dm = daemon_manager.DaemonManager(
-        binary_file.name, daemon_target=long_running_daemon
+        binary_file.name,
+        daemon_target=long_running_daemon,
+        cclient=fake_cclient,
     )
     dm.start()
 
-    with self.assertRaises(SystemExit) as cm:
+    with self.assertRaises(SystemExit):
       dm.reboot()
       self.assertEqual(cm.exception.code, 1)
+    self._assert_error_event_logged(
+        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR
+    )
 
   def assert_run_simple_daemon_success(self):
     damone_output_file = tempfile.NamedTemporaryFile(
@@ -374,6 +439,33 @@
       f.write(str(p.pid))
     return p
 
+  def _assert_error_event_logged(self, fake_cclient, error_type):
+    error_events = fake_cclient.get_sent_events()
+    self.assertEquals(len(error_events), 1)
+    self.assertEquals(
+        edit_event_pb2.EditEvent.FromString(
+            error_events[0].source_extension
+        ).edit_monitor_error_event.error_type,
+        error_type,
+    )
+
+
+class FakeClearcutClient:
+
+  def __init__(self):
+    self.pending_log_events = []
+    self.sent_log_event = []
+
+  def log(self, log_event):
+    self.pending_log_events.append(log_event)
+
+  def flush_events(self):
+    self.sent_log_event.extend(self.pending_log_events)
+    self.pending_log_events.clear()
+
+  def get_sent_events(self):
+    return self.sent_log_event + self.pending_log_events
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/edit_monitor/edit_monitor.py b/tools/edit_monitor/edit_monitor.py
new file mode 100644
index 0000000..ab528e8
--- /dev/null
+++ b/tools/edit_monitor/edit_monitor.py
@@ -0,0 +1,220 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import getpass
+import logging
+import multiprocessing.connection
+import os
+import pathlib
+import platform
+import threading
+import time
+
+from atest.metrics import clearcut_client
+from atest.proto import clientanalytics_pb2
+from proto import edit_event_pb2
+from watchdog.events import FileSystemEvent
+from watchdog.events import PatternMatchingEventHandler
+from watchdog.observers import Observer
+
+# Enum of the Clearcut log source defined under
+# /google3/wireless/android/play/playlog/proto/log_source_enum.proto
+LOG_SOURCE = 2524
+DEFAULT_FLUSH_INTERVAL_SECONDS = 5
+DEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD = 100
+
+
+class ClearcutEventHandler(PatternMatchingEventHandler):
+
+  def __init__(
+      self,
+      path: str,
+      flush_interval_sec: int,
+      single_events_size_threshold: int,
+      is_dry_run: bool = False,
+      cclient: clearcut_client.Clearcut | None = None,
+  ):
+
+    super().__init__(patterns=["*"], ignore_directories=True)
+    self.root_monitoring_path = path
+    self.flush_interval_sec = flush_interval_sec
+    self.single_events_size_threshold = single_events_size_threshold
+    self.is_dry_run = is_dry_run
+    self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE)
+
+    self.user_name = getpass.getuser()
+    self.host_name = platform.node()
+    self.source_root = os.environ.get("ANDROID_BUILD_TOP", "")
+
+    self.pending_events = []
+    self._scheduled_log_thread = None
+    self._pending_events_lock = threading.Lock()
+
+  def on_moved(self, event: FileSystemEvent):
+    self._log_edit_event(event, edit_event_pb2.EditEvent.MOVE)
+
+  def on_created(self, event: FileSystemEvent):
+    self._log_edit_event(event, edit_event_pb2.EditEvent.CREATE)
+
+  def on_deleted(self, event: FileSystemEvent):
+    self._log_edit_event(event, edit_event_pb2.EditEvent.DELETE)
+
+  def on_modified(self, event: FileSystemEvent):
+    self._log_edit_event(event, edit_event_pb2.EditEvent.MODIFY)
+
+  def flushall(self):
+    logging.info("flushing all pending events.")
+    if self._scheduled_log_thread:
+      logging.info("canceling log thread")
+      self._scheduled_log_thread.cancel()
+      self._scheduled_log_thread = None
+
+    self._log_clearcut_events()
+    self.cclient.flush_events()
+
+  def _log_edit_event(
+      self, event: FileSystemEvent, edit_type: edit_event_pb2.EditEvent.EditType
+  ):
+    try:
+      event_time = time.time()
+
+      if self._is_hidden_file(pathlib.Path(event.src_path)):
+        logging.debug("ignore hidden file: %s.", event.src_path)
+        return
+
+      if not self._is_under_git_project(pathlib.Path(event.src_path)):
+        logging.debug(
+            "ignore file %s which does not belong to a git project",
+            event.src_path,
+        )
+        return
+
+      logging.info("%s: %s", event.event_type, event.src_path)
+
+      event_proto = edit_event_pb2.EditEvent(
+          user_name=self.user_name,
+          host_name=self.host_name,
+          source_root=self.source_root,
+      )
+      event_proto.single_edit_event.CopyFrom(
+          edit_event_pb2.EditEvent.SingleEditEvent(
+              file_path=event.src_path, edit_type=edit_type
+          )
+      )
+      with self._pending_events_lock:
+        self.pending_events.append((event_proto, event_time))
+        if not self._scheduled_log_thread:
+          logging.debug(
+              "Scheduling thread to run in %d seconds", self.flush_interval_sec
+          )
+          self._scheduled_log_thread = threading.Timer(
+              self.flush_interval_sec, self._log_clearcut_events
+          )
+          self._scheduled_log_thread.start()
+
+    except Exception:
+      logging.exception("Failed to log edit event.")
+
+  def _is_hidden_file(self, file_path: pathlib.Path) -> bool:
+    return any(
+        part.startswith(".")
+        for part in file_path.relative_to(self.root_monitoring_path).parts
+    )
+
+  def _is_under_git_project(self, file_path: pathlib.Path) -> bool:
+    root_path = pathlib.Path(self.root_monitoring_path).resolve()
+    return any(
+        root_path.joinpath(dir).joinpath('.git').exists()
+        for dir in file_path.relative_to(root_path).parents
+    )
+
+  def _log_clearcut_events(self):
+    with self._pending_events_lock:
+      self._scheduled_log_thread = None
+      edit_events = self.pending_events
+      self.pending_events = []
+
+    pending_events_size = len(edit_events)
+    if pending_events_size > self.single_events_size_threshold:
+      logging.info(
+          "got %d events in %d seconds, sending aggregated events instead",
+          pending_events_size,
+          self.flush_interval_sec,
+      )
+      aggregated_event_time = edit_events[0][1]
+      aggregated_event_proto = edit_event_pb2.EditEvent(
+          user_name=self.user_name,
+          host_name=self.host_name,
+          source_root=self.source_root,
+      )
+      aggregated_event_proto.aggregated_edit_event.CopyFrom(
+          edit_event_pb2.EditEvent.AggregatedEditEvent(
+              num_edits=pending_events_size
+          )
+      )
+      edit_events = [(aggregated_event_proto, aggregated_event_time)]
+
+    if self.is_dry_run:
+      logging.info("Sent %d edit events in dry run.", len(edit_events))
+      return
+
+    for event_proto, event_time in edit_events:
+      log_event = clientanalytics_pb2.LogEvent(
+          event_time_ms=int(event_time * 1000),
+          source_extension=event_proto.SerializeToString(),
+      )
+      self.cclient.log(log_event)
+
+    logging.info("sent %d edit events", len(edit_events))
+
+
+def start(
+    path: str,
+    is_dry_run: bool = False,
+    flush_interval_sec: int = DEFAULT_FLUSH_INTERVAL_SECONDS,
+    single_events_size_threshold: int = DEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD,
+    cclient: clearcut_client.Clearcut | None = None,
+    pipe_sender: multiprocessing.connection.Connection | None = None,
+):
+  """Method to start the edit monitor.
+
+  This is the entry point to start the edit monitor as a subprocess of
+  the daemon manager.
+
+  params:
+    path: The root path to monitor
+    cclient: The clearcut client to send the edit logs.
+    conn: the sender of the pipe to communicate with the deamon manager.
+  """
+  event_handler = ClearcutEventHandler(
+      path, flush_interval_sec, single_events_size_threshold, is_dry_run, cclient)
+  observer = Observer()
+
+  logging.info("Starting observer on path %s.", path)
+  observer.schedule(event_handler, path, recursive=True)
+  observer.start()
+  logging.info("Observer started.")
+  if pipe_sender:
+    pipe_sender.send("Observer started.")
+
+  try:
+    while True:
+      time.sleep(1)
+  finally:
+    event_handler.flushall()
+    observer.stop()
+    observer.join()
+    if pipe_sender:
+      pipe_sender.close()
diff --git a/tools/edit_monitor/edit_monitor_integration_test.py b/tools/edit_monitor/edit_monitor_integration_test.py
new file mode 100644
index 0000000..3d28274
--- /dev/null
+++ b/tools/edit_monitor/edit_monitor_integration_test.py
@@ -0,0 +1,141 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Integration tests for Edit Monitor."""
+
+import glob
+import logging
+import os
+import pathlib
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import unittest
+
+from importlib import resources
+from unittest import mock
+
+
+class EditMonitorIntegrationTest(unittest.TestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    super().setUpClass()
+    # Configure to print logging to stdout.
+    logging.basicConfig(filename=None, level=logging.DEBUG)
+    console = logging.StreamHandler(sys.stdout)
+    logging.getLogger("").addHandler(console)
+
+  def setUp(self):
+    super().setUp()
+    self.working_dir = tempfile.TemporaryDirectory()
+    self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath(
+        "files"
+    )
+    self.root_monitoring_path.mkdir()
+    self.edit_monitor_binary_path = self._import_executable("edit_monitor")
+    self.patch = mock.patch.dict(
+        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'})
+    self.patch.start()
+
+  def tearDown(self):
+    self.patch.stop()
+    self.working_dir.cleanup()
+    super().tearDown()
+
+  def test_log_single_edit_event_success(self):
+    p = self._start_edit_monitor_process()
+
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath(".git").touch()
+
+    # Create and modify a file.
+    test_file = self.root_monitoring_path.joinpath("test.txt")
+    with open(test_file, "w") as f:
+      f.write("something")
+
+    # Move the file.
+    test_file_moved = self.root_monitoring_path.joinpath("new_test.txt")
+    test_file.rename(test_file_moved)
+
+    # Delete the file.
+    test_file_moved.unlink()
+
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.communicate()
+
+    self.assertEqual(self._get_logged_events_num(), 4)
+
+  def _start_edit_monitor_process(self):
+    command = f"""
+    export TMPDIR="{self.working_dir.name}"
+    {self.edit_monitor_binary_path} --path={self.root_monitoring_path} --dry_run"""
+    p = subprocess.Popen(
+        command,
+        shell=True,
+        text=True,
+        start_new_session=True,
+        executable="/bin/bash",
+    )
+    self._wait_for_observer_start(time_out=5)
+    return p
+
+  def _wait_for_observer_start(self, time_out):
+    start_time = time.time()
+
+    while time.time() < start_time + time_out:
+      log_files = glob.glob(self.working_dir.name + "/edit_monitor_*/*.log")
+      if log_files:
+        with open(log_files[0], "r") as f:
+          for line in f:
+            logging.debug("initial log: %s", line)
+            if line.rstrip("\n").endswith("Observer started."):
+              return
+      else:
+        time.sleep(1)
+
+    self.fail(f"Observer not started in {time_out} seconds.")
+
+  def _get_logged_events_num(self):
+    log_files = glob.glob(self.working_dir.name + "/edit_monitor_*/*.log")
+    self.assertEqual(len(log_files), 1)
+
+    with open(log_files[0], "r") as f:
+      for line in f:
+        logging.debug("complete log: %s", line)
+        if line.rstrip("\n").endswith("in dry run."):
+          return int(line.split(":")[-1].split(" ")[2])
+
+    return 0
+
+  def _import_executable(self, executable_name: str) -> pathlib.Path:
+    binary_dir = pathlib.Path(self.working_dir.name).joinpath("binary")
+    binary_dir.mkdir()
+    executable_path = binary_dir.joinpath(executable_name)
+    with resources.as_file(
+        resources.files("testdata").joinpath(executable_name)
+    ) as binary:
+      shutil.copy(binary, executable_path)
+    executable_path.chmod(0o755)
+    return executable_path
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tools/edit_monitor/edit_monitor_test.py b/tools/edit_monitor/edit_monitor_test.py
new file mode 100644
index 0000000..64a3871
--- /dev/null
+++ b/tools/edit_monitor/edit_monitor_test.py
@@ -0,0 +1,301 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittests for Edit Monitor."""
+
+import logging
+import multiprocessing
+import os
+import pathlib
+import signal
+import sys
+import tempfile
+import time
+import unittest
+
+from atest.proto import clientanalytics_pb2
+from edit_monitor import edit_monitor
+from proto import edit_event_pb2
+
+
+class EditMonitorTest(unittest.TestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    super().setUpClass()
+    # Configure to print logging to stdout.
+    logging.basicConfig(filename=None, level=logging.DEBUG)
+    console = logging.StreamHandler(sys.stdout)
+    logging.getLogger('').addHandler(console)
+
+  def setUp(self):
+    super().setUp()
+    self.working_dir = tempfile.TemporaryDirectory()
+    self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath(
+        'files'
+    )
+    self.root_monitoring_path.mkdir()
+    self.log_event_dir = pathlib.Path(self.working_dir.name).joinpath('logs')
+    self.log_event_dir.mkdir()
+
+  def tearDown(self):
+    self.working_dir.cleanup()
+    super().tearDown()
+
+  def test_log_single_edit_event_success(self):
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath('.git').touch()
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output')
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create and modify a file.
+    test_file = self.root_monitoring_path.joinpath('test.txt')
+    with open(test_file, 'w') as f:
+      f.write('something')
+    # Move the file.
+    test_file_moved = self.root_monitoring_path.joinpath('new_test.txt')
+    test_file.rename(test_file_moved)
+    # Delete the file.
+    test_file_moved.unlink()
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 4)
+    expected_create_event = edit_event_pb2.EditEvent.SingleEditEvent(
+        file_path=str(
+            self.root_monitoring_path.joinpath('test.txt').resolve()
+        ),
+        edit_type=edit_event_pb2.EditEvent.CREATE,
+    )
+    expected_modify_event = edit_event_pb2.EditEvent.SingleEditEvent(
+        file_path=str(
+            self.root_monitoring_path.joinpath('test.txt').resolve()
+        ),
+        edit_type=edit_event_pb2.EditEvent.MODIFY,
+    )
+    expected_move_event = edit_event_pb2.EditEvent.SingleEditEvent(
+        file_path=str(
+            self.root_monitoring_path.joinpath('test.txt').resolve()
+        ),
+        edit_type=edit_event_pb2.EditEvent.MOVE,
+    )
+    expected_delete_event = edit_event_pb2.EditEvent.SingleEditEvent(
+        file_path=str(
+            self.root_monitoring_path.joinpath('new_test.txt').resolve()
+        ),
+        edit_type=edit_event_pb2.EditEvent.DELETE,
+    )
+    self.assertEqual(
+        expected_create_event,
+        edit_event_pb2.EditEvent.FromString(
+            logged_events[0].source_extension
+        ).single_edit_event,
+    )
+    self.assertEqual(
+        expected_modify_event,
+        edit_event_pb2.EditEvent.FromString(
+            logged_events[1].source_extension
+        ).single_edit_event,
+    )
+    self.assertEqual(
+        expected_move_event,
+        edit_event_pb2.EditEvent.FromString(
+            logged_events[2].source_extension
+        ).single_edit_event,
+    )
+    self.assertEqual(
+        expected_delete_event,
+        edit_event_pb2.EditEvent.FromString(
+            logged_events[3].source_extension
+        ).single_edit_event,
+    )
+
+
+  def test_log_aggregated_edit_event_success(self):
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath('.git').touch()
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output')
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create 6 test files
+    for i in range(6):
+      test_file = self.root_monitoring_path.joinpath('test_' + str(i))
+      test_file.touch()
+
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 1)
+
+    expected_aggregated_edit_event = (
+        edit_event_pb2.EditEvent.AggregatedEditEvent(
+            num_edits=6,
+        )
+    )
+
+    self.assertEqual(
+        expected_aggregated_edit_event,
+        edit_event_pb2.EditEvent.FromString(
+            logged_events[0].source_extension
+        ).aggregated_edit_event,
+    )
+
+  def test_do_not_log_edit_event_for_directory_change(self):
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath('.git').touch()
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output')
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create a sub directory
+    self.root_monitoring_path.joinpath('test_dir').mkdir()
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 0)
+
+  def test_do_not_log_edit_event_for_hidden_file(self):
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath('.git').touch()
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output')
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create a hidden file.
+    self.root_monitoring_path.joinpath('.test.txt').touch()
+    # Create a hidden dir.
+    hidden_dir = self.root_monitoring_path.joinpath('.test')
+    hidden_dir.mkdir()
+    hidden_dir.joinpath('test.txt').touch()
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 0)
+
+  def test_do_not_log_edit_event_for_non_git_project_file(self):
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output')
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create a file.
+    self.root_monitoring_path.joinpath('test.txt').touch()
+    # Create a file under a sub dir.
+    sub_dir = self.root_monitoring_path.joinpath('.test')
+    sub_dir.mkdir()
+    sub_dir.joinpath('test.txt').touch()
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 0)
+
+  def test_log_edit_event_fail(self):
+    # Create the .git file under the monitoring dir.
+    self.root_monitoring_path.joinpath('.git').touch()
+    fake_cclient = FakeClearcutClient(
+        log_output_file=self.log_event_dir.joinpath('logs.output'),
+        raise_log_exception=True,
+    )
+    p = self._start_test_edit_monitor_process(fake_cclient)
+
+    # Create a file.
+    self.root_monitoring_path.joinpath('test.txt').touch()
+    # Give some time for the edit monitor to receive the edit event.
+    time.sleep(1)
+    # Stop the edit monitor and flush all events.
+    os.kill(p.pid, signal.SIGINT)
+    p.join()
+
+    logged_events = self._get_logged_events()
+    self.assertEqual(len(logged_events), 0)
+
+  def _start_test_edit_monitor_process(
+      self, cclient
+  ) -> multiprocessing.Process:
+    receiver, sender = multiprocessing.Pipe()
+    # Start edit monitor in a subprocess.
+    p = multiprocessing.Process(
+        target=edit_monitor.start,
+        args=(str(self.root_monitoring_path.resolve()), False, 0.5, 5, cclient, sender),
+    )
+    p.daemon = True
+    p.start()
+
+    # Wait until observer started.
+    received_data = receiver.recv()
+    self.assertEquals(received_data, 'Observer started.')
+
+    receiver.close()
+    return p
+
+  def _get_logged_events(self):
+    with open(self.log_event_dir.joinpath('logs.output'), 'rb') as f:
+      data = f.read()
+
+    return [
+        clientanalytics_pb2.LogEvent.FromString(record)
+        for record in data.split(b'\x00')
+        if record
+    ]
+
+
+class FakeClearcutClient:
+
+  def __init__(self, log_output_file, raise_log_exception=False):
+    self.pending_log_events = []
+    self.raise_log_exception = raise_log_exception
+    self.log_output_file = log_output_file
+
+  def log(self, log_event):
+    if self.raise_log_exception:
+      raise Exception('unknown exception')
+    self.pending_log_events.append(log_event)
+
+  def flush_events(self):
+    delimiter = b'\x00'  # Use a null byte as the delimiter
+    with open(self.log_output_file, 'wb') as f:
+      for log_event in self.pending_log_events:
+        f.write(log_event.SerializeToString() + delimiter)
+
+    self.pending_log_events.clear()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/edit_monitor/main.py b/tools/edit_monitor/main.py
index e69de29..49385f1 100644
--- a/tools/edit_monitor/main.py
+++ b/tools/edit_monitor/main.py
@@ -0,0 +1,118 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import logging
+import os
+import signal
+import sys
+import tempfile
+
+from edit_monitor import daemon_manager
+from edit_monitor import edit_monitor
+
+
+def create_arg_parser():
+  """Creates an instance of the default arg parser."""
+
+  parser = argparse.ArgumentParser(
+      description=(
+          'Monitors edits in Android source code and uploads the edit logs.'
+      ),
+      add_help=True,
+      formatter_class=argparse.RawDescriptionHelpFormatter,
+  )
+
+  parser.add_argument(
+      '--path',
+      type=str,
+      required=True,
+      help='Root path to monitor the edit events.',
+  )
+
+  parser.add_argument(
+      '--dry_run',
+      action='store_true',
+      help='Dry run the edit monitor. This starts the edit monitor process without actually send the edit logs to clearcut.',
+  )
+
+  parser.add_argument(
+      '--force_cleanup',
+      action='store_true',
+      help=(
+          'Instead of start a new edit monitor, force stop all existing edit'
+          ' monitors in the system. This option is only used in emergent cases'
+          ' when we want to prevent user damage by the edit monitor.'
+      ),
+  )
+
+  parser.add_argument(
+      '--verbose',
+      action='store_true',
+      help=(
+          'Log verbose info in the log file for debugging purpose.'
+      ),
+  )
+
+  return parser
+
+
+def configure_logging(verbose=False):
+  root_logging_dir = tempfile.mkdtemp(prefix='edit_monitor_')
+  _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')
+
+  log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
+  date_fmt = '%Y-%m-%d %H:%M:%S'
+  log_level = logging.DEBUG if verbose else logging.INFO
+
+  logging.basicConfig(
+      filename=log_path, level=log_level, format=log_fmt, datefmt=date_fmt
+  )
+  # Filter out logs from inotify_buff to prevent log pollution.
+  logging.getLogger('watchdog.observers.inotify_buffer').addFilter(
+      lambda record: record.filename != 'inotify_buffer.py')
+  print(f'logging to file {log_path}')
+
+
+def term_signal_handler(_signal_number, _frame):
+  logging.info('Process %d received SIGTERM, Terminating...', os.getpid())
+  sys.exit(0)
+
+
+def main(argv: list[str]):
+  args = create_arg_parser().parse_args(argv[1:])
+  configure_logging(args.verbose)
+  if args.dry_run:
+    logging.info('This is a dry run.')
+  dm = daemon_manager.DaemonManager(
+      binary_path=argv[0],
+      daemon_target=edit_monitor.start,
+      daemon_args=(args.path, args.dry_run),
+  )
+
+  if args.force_cleanup:
+    dm.cleanup()
+
+  try:
+    dm.start()
+    dm.monitor_daemon()
+  except Exception:
+    logging.exception('Unexpected exception raised when run daemon.')
+  finally:
+    dm.stop()
+
+
+if __name__ == '__main__':
+  signal.signal(signal.SIGTERM, term_signal_handler)
+  main(sys.argv)
diff --git a/tools/edit_monitor/proto/edit_event.proto b/tools/edit_monitor/proto/edit_event.proto
index b3630bc..dc3d3f6 100644
--- a/tools/edit_monitor/proto/edit_event.proto
+++ b/tools/edit_monitor/proto/edit_event.proto
@@ -36,8 +36,6 @@
   // Event that logs errors happened in the edit monitor.
   message EditMonitorErrorEvent {
     ErrorType error_type = 1;
-    string error_msg = 2;
-    string stack_trace = 3;
   }
 
   // ------------------------
diff --git a/tools/edit_monitor/utils.py b/tools/edit_monitor/utils.py
new file mode 100644
index 0000000..b88949d
--- /dev/null
+++ b/tools/edit_monitor/utils.py
@@ -0,0 +1,53 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import hashlib
+import logging
+import os
+
+
+def is_feature_enabled(
+    feature_name: str,
+    user_name: str,
+    enable_flag: str = None,
+    rollout_percent: int = 100,
+) -> bool:
+  """Determine whether the given feature is enabled.
+
+  Whether a given feature is enabled or not depends on two flags: 1) the
+  enable_flag that explicitly enable/disable the feature and 2) the rollout_flag
+  that controls the rollout percentage.
+
+  Args:
+    feature_name: name of the feature.
+    user_name: system user name.
+    enable_flag: name of the env var that enables/disables the feature
+      explicitly.
+    rollout_flg: name of the env var that controls the rollout percentage, the
+      value stored in the env var should be an int between 0 and 100 string
+  """
+  if enable_flag:
+    if os.environ.get(enable_flag, "") == "false":
+      logging.info("feature: %s is disabled", feature_name)
+      return False
+
+    if os.environ.get(enable_flag, "") == "true":
+      logging.info("feature: %s is enabled", feature_name)
+      return True
+
+  hash_object = hashlib.sha256()
+  hash_object.update((user_name + feature_name).encode("utf-8"))
+  hash_number = int(hash_object.hexdigest(), 16) % 100
+
+  return hash_number < rollout_percent
diff --git a/tools/edit_monitor/utils_test.py b/tools/edit_monitor/utils_test.py
new file mode 100644
index 0000000..1c30aa1
--- /dev/null
+++ b/tools/edit_monitor/utils_test.py
@@ -0,0 +1,71 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittests for edit monitor utils."""
+import os
+import unittest
+from unittest import mock
+
+from edit_monitor import utils
+
+TEST_USER = 'test_user'
+TEST_FEATURE = 'test_feature'
+ENABLE_TEST_FEATURE_FLAG = 'ENABLE_TEST_FEATURE'
+ROLLOUT_TEST_FEATURE_FLAG = 'ROLLOUT_TEST_FEATURE'
+
+
+class EnableFeatureTest(unittest.TestCase):
+
+  def test_feature_enabled_without_flag(self):
+    self.assertTrue(utils.is_feature_enabled(TEST_FEATURE, TEST_USER))
+
+  @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'false'}, clear=True)
+  def test_feature_disabled_with_flag(self):
+    self.assertFalse(
+        utils.is_feature_enabled(
+            TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG
+        )
+    )
+
+  @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'true'}, clear=True)
+  def test_feature_enabled_with_flag(self):
+    self.assertTrue(
+        utils.is_feature_enabled(
+            TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG
+        )
+    )
+
+  def test_feature_enabled_with_rollout_percentage(self):
+    self.assertTrue(
+        utils.is_feature_enabled(
+            TEST_FEATURE,
+            TEST_USER,
+            ENABLE_TEST_FEATURE_FLAG,
+            90,
+        )
+    )
+
+  def test_feature_disabled_with_rollout_percentage(self):
+    self.assertFalse(
+        utils.is_feature_enabled(
+            TEST_FEATURE,
+            TEST_USER,
+            ENABLE_TEST_FEATURE_FLAG,
+            10,
+        )
+    )
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/filelistdiff/file_list_diff.py b/tools/filelistdiff/file_list_diff.py
index fbbfedf..a6408e8 100644
--- a/tools/filelistdiff/file_list_diff.py
+++ b/tools/filelistdiff/file_list_diff.py
@@ -76,7 +76,7 @@
     parser.add_argument('kati_installed_file_list')
     parser.add_argument('soong_installed_file_list')
     parser.add_argument('system_module_name')
-    parser.add_argument('--allowlists', nargs='+')
+    parser.add_argument('--allowlists', nargs='*', default=[])
     args = parser.parse_args()
 
     find_unique_items(args.kati_installed_file_list, args.soong_installed_file_list, args.system_module_name, args.allowlists)
\ No newline at end of file
diff --git a/tools/fs_config/Android.bp b/tools/fs_config/Android.bp
index 6aa5289..a5b6fd0 100644
--- a/tools/fs_config/Android.bp
+++ b/tools/fs_config/Android.bp
@@ -277,6 +277,7 @@
     out: ["out"],
 }
 
+// system
 genrule {
     name: "fs_config_dirs_system_gen",
     defaults: ["fs_config_defaults"],
@@ -307,6 +308,7 @@
     src: ":fs_config_files_system_gen",
 }
 
+// system_ext
 genrule {
     name: "fs_config_dirs_system_ext_gen",
     defaults: ["fs_config_defaults"],
@@ -337,6 +339,7 @@
     system_ext_specific: true,
 }
 
+// product
 genrule {
     name: "fs_config_dirs_product_gen",
     defaults: ["fs_config_defaults"],
@@ -367,6 +370,7 @@
     product_specific: true,
 }
 
+// vendor
 genrule {
     name: "fs_config_dirs_vendor_gen",
     defaults: ["fs_config_defaults"],
@@ -397,6 +401,7 @@
     vendor: true,
 }
 
+// odm
 genrule {
     name: "fs_config_dirs_odm_gen",
     defaults: ["fs_config_defaults"],
@@ -427,4 +432,214 @@
     device_specific: true,
 }
 
-// TODO(jiyong): add fs_config for oem, system_dlkm, vendor_dlkm, odm_dlkm partitions
+// system_dlkm
+genrule {
+    name: "fs_config_dirs_system_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_dirs +
+        "--partition system_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_dirs_system_dlkm",
+    filename: "fs_config_dirs",
+    src: ":fs_config_dirs_system_dlkm_gen",
+    system_dlkm_specific: true,
+}
+
+genrule {
+    name: "fs_config_files_system_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_files +
+        "--partition system_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_files_system_dlkm",
+    filename: "fs_config_files",
+    src: ":fs_config_files_system_dlkm_gen",
+    system_dlkm_specific: true,
+}
+
+// vendor_dlkm
+genrule {
+    name: "fs_config_dirs_vendor_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_dirs +
+        "--partition vendor_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_dirs_vendor_dlkm",
+    filename: "fs_config_dirs",
+    src: ":fs_config_dirs_vendor_dlkm_gen",
+    vendor_dlkm_specific: true,
+}
+
+genrule {
+    name: "fs_config_files_vendor_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_files +
+        "--partition vendor_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_files_vendor_dlkm",
+    filename: "fs_config_files",
+    src: ":fs_config_files_vendor_dlkm_gen",
+    vendor_dlkm_specific: true,
+}
+
+// odm_dlkm
+genrule {
+    name: "fs_config_dirs_odm_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_dirs +
+        "--partition odm_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_dirs_odm_dlkm",
+    filename: "fs_config_dirs",
+    src: ":fs_config_dirs_odm_dlkm_gen",
+    odm_dlkm_specific: true,
+}
+
+genrule {
+    name: "fs_config_files_odm_dlkm_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_files +
+        "--partition odm_dlkm " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_files_odm_dlkm",
+    filename: "fs_config_files",
+    src: ":fs_config_files_odm_dlkm_gen",
+    odm_dlkm_specific: true,
+}
+
+// oem
+genrule {
+    name: "fs_config_dirs_oem_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_dirs +
+        "--partition oem " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_dirs_oem",
+    filename: "fs_config_dirs",
+    src: ":fs_config_dirs_oem_gen",
+    oem_specific: true,
+}
+
+genrule {
+    name: "fs_config_files_oem_gen",
+    defaults: ["fs_config_defaults"],
+    cmd: fs_config_cmd_files +
+        "--partition oem " +
+        "$(locations :target_fs_config_gen)",
+}
+
+prebuilt_etc {
+    name: "fs_config_files_oem",
+    filename: "fs_config_files",
+    src: ":fs_config_files_oem_gen",
+    oem_specific: true,
+}
+
+// Generate the <p>/etc/fs_config_dirs binary files for each partition.
+// Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable.
+phony {
+    name: "fs_config_dirs",
+    required: [
+        "fs_config_dirs_system",
+        "fs_config_dirs_system_ext",
+        "fs_config_dirs_product",
+        "fs_config_dirs_nonsystem",
+    ],
+}
+
+// Generate the <p>/etc/fs_config_files binary files for each partition.
+// Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable.
+phony {
+    name: "fs_config_files",
+    required: [
+        "fs_config_files_system",
+        "fs_config_files_system_ext",
+        "fs_config_files_product",
+        "fs_config_files_nonsystem",
+    ],
+}
+
+// Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions
+// excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to
+// PRODUCT_PACKAGES in the device make file to enable.
+phony {
+    name: "fs_config_dirs_nonsystem",
+    required: [] +
+        select(soong_config_variable("fs_config", "vendor"), {
+            true: ["fs_config_dirs_vendor"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "oem"), {
+            true: ["fs_config_dirs_oem"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "odm"), {
+            true: ["fs_config_dirs_odm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "vendor_dlkm"), {
+            true: ["fs_config_dirs_vendor_dlkm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "odm_dlkm"), {
+            true: ["fs_config_dirs_odm_dlkm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "system_dlkm"), {
+            true: ["fs_config_dirs_system_dlkm"],
+            default: [],
+        }),
+}
+
+// Generate the <p>/etc/fs_config_files binary files for all enabled partitions
+// excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to
+// PRODUCT_PACKAGES in the device make file to enable.
+phony {
+    name: "fs_config_files_nonsystem",
+    required: [] +
+        select(soong_config_variable("fs_config", "vendor"), {
+            true: ["fs_config_files_vendor"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "oem"), {
+            true: ["fs_config_files_oem"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "odm"), {
+            true: ["fs_config_files_odm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "vendor_dlkm"), {
+            true: ["fs_config_files_vendor_dlkm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "odm_dlkm"), {
+            true: ["fs_config_files_odm_dlkm"],
+            default: [],
+        }) +
+        select(soong_config_variable("fs_config", "system_dlkm"), {
+            true: ["fs_config_files_system_dlkm"],
+            default: [],
+        }),
+}
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
deleted file mode 100644
index e4c3626..0000000
--- a/tools/fs_config/Android.mk
+++ /dev/null
@@ -1,328 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# One can override the default android_filesystem_config.h file by using TARGET_FS_CONFIG_GEN.
-#   Set TARGET_FS_CONFIG_GEN to contain a list of intermediate format files
-#   for generating the android_filesystem_config.h file.
-#
-# More information can be found in the README
-
-ifneq ($(wildcard $(TARGET_DEVICE_DIR)/android_filesystem_config.h),)
-$(error Using $(TARGET_DEVICE_DIR)/android_filesystem_config.h is deprecated, please use TARGET_FS_CONFIG_GEN instead)
-endif
-
-android_filesystem_config := system/core/libcutils/include/private/android_filesystem_config.h
-capability_header := bionic/libc/kernel/uapi/linux/capability.h
-
-# List of supported vendor, oem, odm, vendor_dlkm, odm_dlkm, and system_dlkm Partitions
-fs_config_generate_extra_partition_list := $(strip \
-  $(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \
-  $(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \
-  $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm) \
-  $(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),vendor_dlkm) \
-  $(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),odm_dlkm) \
-  $(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),system_dlkm) \
-)
-
-##################################
-# Generate the <p>/etc/fs_config_dirs binary files for each partition.
-# Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_REQUIRED_MODULES := \
-  fs_config_dirs_system \
-  fs_config_dirs_system_ext \
-  fs_config_dirs_product \
-  fs_config_dirs_nonsystem
-include $(BUILD_PHONY_PACKAGE)
-
-##################################
-# Generate the <p>/etc/fs_config_files binary files for each partition.
-# Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_REQUIRED_MODULES := \
-  fs_config_files_system \
-  fs_config_files_system_ext \
-  fs_config_files_product \
-  fs_config_files_nonsystem
-include $(BUILD_PHONY_PACKAGE)
-
-##################################
-# Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions
-# excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to
-# PRODUCT_PACKAGES in the device make file to enable.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs_nonsystem
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_dirs_$(t))
-include $(BUILD_PHONY_PACKAGE)
-
-##################################
-# Generate the <p>/etc/fs_config_files binary files for all enabled partitions
-# excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to
-# PRODUCT_PACKAGES in the device make file to enable.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files_nonsystem
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_files_$(t))
-include $(BUILD_PHONY_PACKAGE)
-
-ifneq ($(filter oem,$(fs_config_generate_extra_partition_list)),)
-##################################
-# Generate the oem/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs_oem
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
-LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition oem \
-	   --dirs \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-##################################
-# Generate the oem/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files_oem
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_files
-LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition oem \
-	   --files \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-endif
-
-ifneq ($(filter vendor_dlkm,$(fs_config_generate_extra_partition_list)),)
-##################################
-# Generate the vendor_dlkm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES in
-# the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs_vendor_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition vendor_dlkm \
-	   --dirs \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-##################################
-# Generate the vendor_dlkm/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES in
-# the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files_vendor_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_files
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition vendor_dlkm \
-	   --files \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-endif
-
-ifneq ($(filter odm_dlkm,$(fs_config_generate_extra_partition_list)),)
-##################################
-# Generate the odm_dlkm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs_odm_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
-LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition odm_dlkm \
-	   --dirs \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-##################################
-# Generate the odm_dlkm/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files_odm_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_files
-LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition odm_dlkm \
-	   --files \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-endif
-
-ifneq ($(filter system_dlkm,$(fs_config_generate_extra_partition_list)),)
-##################################
-# Generate the system_dlkm/etc/fs_config_dirs binary file for the target
-# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_dirs_system_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
-LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition system_dlkm \
-	   --dirs \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-##################################
-# Generate the system_dlkm/etc/fs_config_files binary file for the target
-# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES
-# in the device make file to enable
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fs_config_files_system_dlkm
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_INSTALLED_MODULE_STEM := fs_config_files
-LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_DLKM)/etc
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config)
-$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header)
-$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header)
-	@mkdir -p $(dir $@)
-	$< fsconfig \
-	   --aid-header $(PRIVATE_ANDROID_FS_HDR) \
-	   --capability-header $(PRIVATE_ANDROID_CAP_HDR) \
-	   --partition system_dlkm \
-	   --files \
-	   --out_file $@ \
-	   $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null)
-
-endif
-
-android_filesystem_config :=
-capability_header :=
-fs_config_generate_extra_partition_list :=
diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go
index ec937fe..c7cf5ed 100644
--- a/tools/ide_query/ide_query.go
+++ b/tools/ide_query/ide_query.go
@@ -293,11 +293,19 @@
 // If a file is covered by multiple modules, the first module is returned.
 func findJavaModules(paths []string, modules map[string]*javaModule) map[string]string {
 	ret := make(map[string]string)
-	for name, module := range modules {
+	// A file may be part of multiple modules. To make the result deterministic,
+	// check the modules in sorted order.
+	keys := make([]string, 0, len(modules))
+	for name := range modules {
+		keys = append(keys, name)
+	}
+	slices.Sort(keys)
+	for _, name := range keys {
 		if strings.HasSuffix(name, ".impl") {
 			continue
 		}
 
+		module := modules[name]
 		for i, p := range paths {
 			if slices.Contains(module.Srcs, p) {
 				ret[p] = name
diff --git a/tools/ide_query/prober_scripts/ide_query.out b/tools/ide_query/prober_scripts/ide_query.out
index cd7ce6d..be48da1 100644
--- a/tools/ide_query/prober_scripts/ide_query.out
+++ b/tools/ide_query/prober_scripts/ide_query.out
@@ -1,7 +1,9 @@
 
-out–a
-8build/make/tools/ide_query/prober_scripts/cpp/general.cc8prebuilts/clang/host/linux-x86/clang-r522817/bin/clang++-mthumb-Os-fomit-frame-pointer-mllvm-enable-shrink-wrap=false-O2-Wall-Wextra-Winit-self-Wpointer-arith-Wunguarded-availability-Werror=date-time-Werror=int-conversion-Werror=pragma-pack&-Werror=pragma-pack-suspicious-include-Werror=sizeof-array-div-Werror=string-plus-int'-Werror=unreachable-code-loop-increment"-Wno-error=deprecated-declarations-Wno-c99-designator-Wno-gnu-folding-constant"-Wno-inconsistent-missing-override-Wno-error=reorder-init-list-Wno-reorder-init-list-Wno-sign-compare-Wno-unused	-DANDROID-DNDEBUG-UDEBUG(-D__compiler_offsetof=__builtin_offsetof*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__	-faddrsig-fdebug-default-version=5-fcolor-diagnostics-ffp-contract=off-fno-exceptions-fno-strict-aliasing-fmessage-length=0#-fno-relaxed-template-template-args-gsimple-template-names-gz=zstd-no-canonical-prefixes-Wno-error=format"-fdebug-prefix-map=/proc/self/cwd=-ftrivial-auto-var-init=zero-g-ffunction-sections-fdata-sections-fno-short-enums-funwind-tables-fstack-protector-strong-Wa,--noexecstack-D_FORTIFY_SOURCE=2-Wstrict-aliasing=2-Werror=return-type-Werror=non-virtual-dtor-Werror=address-Werror=sequence-point-Werror=format-security-nostdlibinc-fdebug-info-for-profiling-msoft-float-march=armv7-a-mfloat-abi=softfp
--mfpu=neon/-Ibuild/make/tools/ide_query/prober_scripts/cpp³-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp…-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto-D__LIBC_API__=10000-D__LIBM_API__=10000-D__LIBDL_API__=10000-Iexternal/protobuf/srcY-Iprebuilts/clang/host/linux-x86/clang-r522817/android_libc++/platform/arm/include/c++/v1=-Iprebuilts/clang/host/linux-x86/clang-r522817/include/c++/v1 -Ibionic/libc/async_safe/include-Isystem/logging/liblog/include'-Ibionic/libc/system_properties/include<-Isystem/core/property_service/libpropertyinfoparser/include-isystembionic/libc/include-isystembionic/libc/kernel/uapi/asm-arm-isystembionic/libc/kernel/uapi-isystembionic/libc/kernel/android/scsi-isystembionic/libc/kernel/android/uapi-targetarmv7a-linux-androideabi10000-DANDROID_STRICT-fPIE-Werror-Wno-unused-parameter-DGOOGLE_PROTOBUF_NO_RTTI-Wimplicit-fallthrough*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS-Wno-gnu-include-next-fvisibility-inlines-hidden-mllvm-enable-shrink-wrap=false-std=gnu++20	-fno-rtti-Isystem/core/include-Isystem/logging/liblog/include-Isystem/media/audio/include-Ihardware/libhardware/include%-Ihardware/libhardware_legacy/include-Ihardware/ril/include-Iframeworks/native/include"-Iframeworks/native/opengl/include-Iframeworks/av/include-Werror=bool-operation -Werror=format-insufficient-args%-Werror=implicit-int-float-conversion-Werror=int-in-bool-context-Werror=int-to-pointer-cast-Werror=pointer-to-int-cast-Werror=xor-used-as-pow-Wno-void-pointer-to-enum-cast-Wno-void-pointer-to-int-cast-Wno-pointer-to-int-cast-Werror=fortify-source-Wno-unused-variable-Wno-missing-field-initializers-Wno-packed-non-pod-Werror=address-of-temporary+-Werror=incompatible-function-pointer-types-Werror=null-dereference-Werror=return-type"-Wno-tautological-constant-compare$-Wno-tautological-type-limit-compare"-Wno-implicit-int-float-conversion!-Wno-tautological-overlap-compare-Wno-deprecated-copy-Wno-range-loop-construct"-Wno-zero-as-null-pointer-constant)-Wno-deprecated-anon-enum-enum-conversion$-Wno-deprecated-enum-enum-conversion-Wno-pessimizing-move-Wno-non-c-typedef-for-linkage-Wno-align-mismatch"-Wno-error=unused-but-set-variable#-Wno-error=unused-but-set-parameter-Wno-error=deprecated-builtins-Wno-error=deprecated2-Wno-error=single-bit-bitfield-constant-conversion$-Wno-error=enum-constexpr-conversion-Wno-error=invalid-offsetof&-Wno-deprecated-dynamic-exception-spec8build/make/tools/ide_query/prober_scripts/cpp/general.cc"Õ?
+out2x
+8build/make/tools/ide_query/prober_scripts/cpp/general.cc8build/make/tools/ide_query/prober_scripts/cpp/general.cc:—"
+8build/make/tools/ide_query/prober_scripts/cpp/general.cc8build/make/tools/ide_query/prober_scripts/cpp/general.cc"8prebuilts/clang/host/linux-x86/clang-r530567/bin/clang++"-nostdlibinc"-mthumb"-Os"-fomit-frame-pointer"-mllvm"-enable-shrink-wrap=false"-O2"-Wall"-Wextra"-Winit-self"-Wpointer-arith"-Wunguarded-availability"-Werror=date-time"-Werror=int-conversion"-Werror=pragma-pack"&-Werror=pragma-pack-suspicious-include"-Werror=sizeof-array-div"-Werror=string-plus-int"'-Werror=unreachable-code-loop-increment""-Wno-error=deprecated-declarations"-Wno-c23-extensions"-Wno-c99-designator"-Wno-gnu-folding-constant""-Wno-inconsistent-missing-override"-Wno-error=reorder-init-list"-Wno-reorder-init-list"-Wno-sign-compare"-Wno-unused"	-DANDROID"-DNDEBUG"-UDEBUG"(-D__compiler_offsetof=__builtin_offsetof"*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__"	-faddrsig"-fdebug-default-version=5"-fcolor-diagnostics"-ffp-contract=off"-fno-exceptions"-fno-strict-aliasing"-fmessage-length=0"-gsimple-template-names"-gz=zstd"-no-canonical-prefixes""-fdebug-prefix-map=/proc/self/cwd="-ftrivial-auto-var-init=zero"-g"-ffunction-sections"-fdata-sections"-fno-short-enums"-funwind-tables"-fstack-protector-strong"-Wa,--noexecstack"-D_FORTIFY_SOURCE=2"-Wstrict-aliasing=2"-Werror=return-type"-Werror=non-virtual-dtor"-Werror=address"-Werror=sequence-point"-Werror=format-security"-msoft-float"-march=armv7-a"-mfloat-abi=softfp"
+-mfpu=neon"/-Ibuild/make/tools/ide_query/prober_scripts/cpp"³-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp"…-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto"-D__LIBC_API__=10000"-D__LIBM_API__=10000"-D__LIBDL_API__=10000"-Iexternal/protobuf/src"Y-Iprebuilts/clang/host/linux-x86/clang-r530567/android_libc++/platform/arm/include/c++/v1"=-Iprebuilts/clang/host/linux-x86/clang-r530567/include/c++/v1" -Ibionic/libc/async_safe/include"-Isystem/logging/liblog/include"'-Ibionic/libc/system_properties/include"<-Isystem/core/property_service/libpropertyinfoparser/include"-isystem"bionic/libc/include"-isystem"bionic/libc/kernel/uapi/asm-arm"-isystem"bionic/libc/kernel/uapi"-isystem"bionic/libc/kernel/android/scsi"-isystem"bionic/libc/kernel/android/uapi"-target"armv7a-linux-androideabi10000"-DANDROID_STRICT"-fPIE"-Werror"-Wno-unused-parameter"-DGOOGLE_PROTOBUF_NO_RTTI"-Wimplicit-fallthrough"*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS"-Wno-gnu-include-next"-fvisibility-inlines-hidden"-mllvm"-enable-shrink-wrap=false"-std=gnu++20"	-fno-rtti"-Isystem/core/include"-Isystem/logging/liblog/include"-Isystem/media/audio/include"-Ihardware/libhardware/include"%-Ihardware/libhardware_legacy/include"-Ihardware/ril/include"-Iframeworks/native/include""-Iframeworks/native/opengl/include"-Iframeworks/av/include"-Werror=bool-operation" -Werror=format-insufficient-args"%-Werror=implicit-int-float-conversion"-Werror=int-in-bool-context"-Werror=int-to-pointer-cast"-Werror=pointer-to-int-cast"-Werror=xor-used-as-pow"-Wno-void-pointer-to-enum-cast"-Wno-void-pointer-to-int-cast"-Wno-pointer-to-int-cast"-Werror=fortify-source"-Wno-unused-variable"-Wno-missing-field-initializers"-Wno-packed-non-pod"-Werror=address-of-temporary"+-Werror=incompatible-function-pointer-types"-Werror=null-dereference"-Werror=return-type""-Wno-tautological-constant-compare"$-Wno-tautological-type-limit-compare""-Wno-implicit-int-float-conversion"!-Wno-tautological-overlap-compare"-Wno-deprecated-copy"-Wno-range-loop-construct""-Wno-zero-as-null-pointer-constant")-Wno-deprecated-anon-enum-enum-conversion"$-Wno-deprecated-enum-enum-conversion"-Wno-error=pessimizing-move"-Wno-non-c-typedef-for-linkage"-Wno-align-mismatch""-Wno-error=unused-but-set-variable"#-Wno-error=unused-but-set-parameter"-Wno-error=deprecated-builtins"-Wno-error=deprecated"&-Wno-deprecated-dynamic-exception-spec"$-Wno-error=enum-constexpr-conversion"-Wno-error=invalid-offsetof")-Wno-error=thread-safety-reference-return"-Wno-vla-cxx-extension"8build/make/tools/ide_query/prober_scripts/cpp/general.cc2Egenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc:Ÿ@
+Egenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc*Õ?
 ¶soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp/foo.pb.h™>// Generated by the protocol buffer compiler.  DO NOT EDIT!
 // source: build/make/tools/ide_query/prober_scripts/cpp/foo.proto
 
diff --git a/tools/ide_query/prober_scripts/regen.sh b/tools/ide_query/prober_scripts/regen.sh
index 2edfe53..04a0264 100755
--- a/tools/ide_query/prober_scripts/regen.sh
+++ b/tools/ide_query/prober_scripts/regen.sh
@@ -21,13 +21,8 @@
 # ide_query.sh. The prober doesn't check-out the full source code, so it
 # can't run ide_query.sh itself.
 
-cd $(dirname $BASH_SOURCE)
-source $(pwd)/../../../shell_utils.sh
-require_top
-
 files_to_build=(
   build/make/tools/ide_query/prober_scripts/cpp/general.cc
 )
 
-cd ${TOP}
 build/make/tools/ide_query/ide_query.sh --lunch_target=aosp_arm-trunk_staging-eng ${files_to_build[@]} > build/make/tools/ide_query/prober_scripts/ide_query.out
diff --git a/tools/metadata/Android.bp b/tools/metadata/Android.bp
deleted file mode 100644
index 77d106d..0000000
--- a/tools/metadata/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-blueprint_go_binary {
-    name: "metadata",
-    deps: [
-            "soong-testing-test_spec_proto",
-            "soong-testing-code_metadata_proto",
-            "soong-testing-code_metadata_internal_proto",
-            "golang-protobuf-proto",
-        ],
-    srcs: [
-        "generator.go",
-    ]
-}
\ No newline at end of file
diff --git a/tools/metadata/OWNERS b/tools/metadata/OWNERS
deleted file mode 100644
index 03bcdf1..0000000
--- a/tools/metadata/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dariofreni@google.com
-joeo@google.com
-ronish@google.com
-caditya@google.com
diff --git a/tools/metadata/generator.go b/tools/metadata/generator.go
deleted file mode 100644
index b7668be..0000000
--- a/tools/metadata/generator.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package main
-
-import (
-	"flag"
-	"fmt"
-	"io"
-	"log"
-	"os"
-	"sort"
-	"strings"
-	"sync"
-
-	"android/soong/testing/code_metadata_internal_proto"
-	"android/soong/testing/code_metadata_proto"
-	"android/soong/testing/test_spec_proto"
-	"google.golang.org/protobuf/proto"
-)
-
-type keyToLocksMap struct {
-	locks sync.Map
-}
-
-func (kl *keyToLocksMap) GetLockForKey(key string) *sync.Mutex {
-	mutex, _ := kl.locks.LoadOrStore(key, &sync.Mutex{})
-	return mutex.(*sync.Mutex)
-}
-
-// Define a struct to hold the combination of team ID and multi-ownership flag for validation
-type sourceFileAttributes struct {
-	TeamID         string
-	MultiOwnership bool
-	Path           string
-}
-
-func getSortedKeys(syncMap *sync.Map) []string {
-	var allKeys []string
-	syncMap.Range(
-		func(key, _ interface{}) bool {
-			allKeys = append(allKeys, key.(string))
-			return true
-		},
-	)
-
-	sort.Strings(allKeys)
-	return allKeys
-}
-
-// writeProtoToFile marshals a protobuf message and writes it to a file
-func writeProtoToFile(outputFile string, message proto.Message) {
-	data, err := proto.Marshal(message)
-	if err != nil {
-		log.Fatal(err)
-	}
-	file, err := os.Create(outputFile)
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer file.Close()
-
-	_, err = file.Write(data)
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-func readFileToString(filePath string) string {
-	file, err := os.Open(filePath)
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer file.Close()
-
-	data, err := io.ReadAll(file)
-	if err != nil {
-		log.Fatal(err)
-	}
-	return string(data)
-}
-
-func writeEmptyOutputProto(outputFile string, metadataRule string) {
-	file, err := os.Create(outputFile)
-	if err != nil {
-		log.Fatal(err)
-	}
-	var message proto.Message
-	if metadataRule == "test_spec" {
-		message = &test_spec_proto.TestSpec{}
-	} else if metadataRule == "code_metadata" {
-		message = &code_metadata_proto.CodeMetadata{}
-	}
-	data, err := proto.Marshal(message)
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer file.Close()
-
-	_, err = file.Write([]byte(data))
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-func processTestSpecProtobuf(
-	filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
-	errCh chan error, wg *sync.WaitGroup,
-) {
-	defer wg.Done()
-
-	fileContent := strings.TrimRight(readFileToString(filePath), "\n")
-	testData := test_spec_proto.TestSpec{}
-	err := proto.Unmarshal([]byte(fileContent), &testData)
-	if err != nil {
-		errCh <- err
-		return
-	}
-
-	ownershipMetadata := testData.GetOwnershipMetadataList()
-	for _, metadata := range ownershipMetadata {
-		key := metadata.GetTargetName()
-		lock := keyLocks.GetLockForKey(key)
-		lock.Lock()
-
-		value, loaded := ownershipMetadataMap.LoadOrStore(
-			key, []*test_spec_proto.TestSpec_OwnershipMetadata{metadata},
-		)
-		if loaded {
-			existingMetadata := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
-			isDuplicate := false
-			for _, existing := range existingMetadata {
-				if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() {
-					errCh <- fmt.Errorf(
-						"Conflicting trendy team IDs found for %s at:\n%s with teamId"+
-							": %s,\n%s with teamId: %s",
-						key,
-						metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(),
-						existing.GetTrendyTeamId(),
-					)
-
-					lock.Unlock()
-					return
-				}
-				if metadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && metadata.GetPath() == existing.GetPath() {
-					isDuplicate = true
-					break
-				}
-			}
-			if !isDuplicate {
-				existingMetadata = append(existingMetadata, metadata)
-				ownershipMetadataMap.Store(key, existingMetadata)
-			}
-		}
-
-		lock.Unlock()
-	}
-}
-
-// processCodeMetadataProtobuf processes CodeMetadata protobuf files
-func processCodeMetadataProtobuf(
-	filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap,
-	errCh chan error, wg *sync.WaitGroup,
-) {
-	defer wg.Done()
-
-	fileContent := strings.TrimRight(readFileToString(filePath), "\n")
-	internalCodeData := code_metadata_internal_proto.CodeMetadataInternal{}
-	err := proto.Unmarshal([]byte(fileContent), &internalCodeData)
-	if err != nil {
-		errCh <- err
-		return
-	}
-
-	// Process each TargetOwnership entry
-	for _, internalMetadata := range internalCodeData.GetTargetOwnershipList() {
-		key := internalMetadata.GetTargetName()
-		lock := keyLocks.GetLockForKey(key)
-		lock.Lock()
-
-		for _, srcFile := range internalMetadata.GetSourceFiles() {
-			srcFileKey := srcFile
-			srcFileLock := keyLocks.GetLockForKey(srcFileKey)
-			srcFileLock.Lock()
-			attributes := sourceFileAttributes{
-				TeamID:         internalMetadata.GetTrendyTeamId(),
-				MultiOwnership: internalMetadata.GetMultiOwnership(),
-				Path:           internalMetadata.GetPath(),
-			}
-
-			existingAttributes, exists := sourceFileMetadataMap.Load(srcFileKey)
-			if exists {
-				existing := existingAttributes.(sourceFileAttributes)
-				if attributes.TeamID != existing.TeamID && (!attributes.MultiOwnership || !existing.MultiOwnership) {
-					errCh <- fmt.Errorf(
-						"Conflict found for source file %s covered at %s with team ID: %s. Existing team ID: %s and path: %s."+
-							" If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+
-							"Multiple-ownership in general is discouraged though as it make infrastructure around android relying on this information pick up a random value when it needs only one.",
-						srcFile, internalMetadata.GetPath(), attributes.TeamID, existing.TeamID, existing.Path,
-					)
-					srcFileLock.Unlock()
-					lock.Unlock()
-					return
-				}
-			} else {
-				// Store the metadata if no conflict
-				sourceFileMetadataMap.Store(srcFileKey, attributes)
-			}
-			srcFileLock.Unlock()
-		}
-
-		value, loaded := ownershipMetadataMap.LoadOrStore(
-			key, []*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{internalMetadata},
-		)
-		if loaded {
-			existingMetadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
-			isDuplicate := false
-			for _, existing := range existingMetadata {
-				if internalMetadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && internalMetadata.GetPath() == existing.GetPath() {
-					isDuplicate = true
-					break
-				}
-			}
-			if !isDuplicate {
-				existingMetadata = append(existingMetadata, internalMetadata)
-				ownershipMetadataMap.Store(key, existingMetadata)
-			}
-		}
-
-		lock.Unlock()
-	}
-}
-
-func main() {
-	inputFile := flag.String("inputFile", "", "Input file path")
-	outputFile := flag.String("outputFile", "", "Output file path")
-	rule := flag.String(
-		"rule", "", "Metadata rule (Hint: test_spec or code_metadata)",
-	)
-	flag.Parse()
-
-	if *inputFile == "" || *outputFile == "" || *rule == "" {
-		fmt.Println("Usage: metadata -rule <rule> -inputFile <input file path> -outputFile <output file path>")
-		os.Exit(1)
-	}
-
-	inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n")
-	filePaths := strings.Split(inputFileData, " ")
-	if len(filePaths) == 1 && filePaths[0] == "" {
-		writeEmptyOutputProto(*outputFile, *rule)
-		return
-	}
-	ownershipMetadataMap := &sync.Map{}
-	keyLocks := &keyToLocksMap{}
-	errCh := make(chan error, len(filePaths))
-	var wg sync.WaitGroup
-
-	switch *rule {
-	case "test_spec":
-		for _, filePath := range filePaths {
-			wg.Add(1)
-			go processTestSpecProtobuf(
-				filePath, ownershipMetadataMap, keyLocks, errCh, &wg,
-			)
-		}
-
-		wg.Wait()
-		close(errCh)
-
-		for err := range errCh {
-			log.Fatal(err)
-		}
-
-		allKeys := getSortedKeys(ownershipMetadataMap)
-		var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata
-
-		for _, key := range allKeys {
-			value, _ := ownershipMetadataMap.Load(key)
-			metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
-			allMetadata = append(allMetadata, metadataList...)
-		}
-
-		testSpec := &test_spec_proto.TestSpec{
-			OwnershipMetadataList: allMetadata,
-		}
-		writeProtoToFile(*outputFile, testSpec)
-		break
-	case "code_metadata":
-		sourceFileMetadataMap := &sync.Map{}
-		for _, filePath := range filePaths {
-			wg.Add(1)
-			go processCodeMetadataProtobuf(
-				filePath, ownershipMetadataMap, sourceFileMetadataMap, keyLocks, errCh, &wg,
-			)
-		}
-
-		wg.Wait()
-		close(errCh)
-
-		for err := range errCh {
-			log.Fatal(err)
-		}
-
-		sortedKeys := getSortedKeys(ownershipMetadataMap)
-		allMetadata := make([]*code_metadata_proto.CodeMetadata_TargetOwnership, 0)
-		for _, key := range sortedKeys {
-			value, _ := ownershipMetadataMap.Load(key)
-			metadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
-			for _, m := range metadata {
-				targetName := m.GetTargetName()
-				path := m.GetPath()
-				trendyTeamId := m.GetTrendyTeamId()
-
-				allMetadata = append(allMetadata, &code_metadata_proto.CodeMetadata_TargetOwnership{
-					TargetName:   &targetName,
-					Path:         &path,
-					TrendyTeamId: &trendyTeamId,
-					SourceFiles:  m.GetSourceFiles(),
-				})
-			}
-		}
-
-		finalMetadata := &code_metadata_proto.CodeMetadata{
-			TargetOwnershipList: allMetadata,
-		}
-		writeProtoToFile(*outputFile, finalMetadata)
-		break
-	default:
-		log.Fatalf("No specific processing implemented for rule '%s'.\n", *rule)
-	}
-}
diff --git a/tools/metadata/go.mod b/tools/metadata/go.mod
deleted file mode 100644
index e9d04b1..0000000
--- a/tools/metadata/go.mod
+++ /dev/null
@@ -1,7 +0,0 @@
-module android/soong/tools/metadata
-
-require google.golang.org/protobuf v0.0.0
-
-replace google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf
-
-go 1.18
\ No newline at end of file
diff --git a/tools/metadata/go.work b/tools/metadata/go.work
deleted file mode 100644
index f2cdf8e..0000000
--- a/tools/metadata/go.work
+++ /dev/null
@@ -1,11 +0,0 @@
-go 1.18
-
-use (
-	.
-	../../../../external/golang-protobuf
-	../../../soong/testing/test_spec_proto
-	../../../soong/testing/code_metadata_proto
-	../../../soong/testing/code_metadata_proto_internal
-)
-
-replace google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
diff --git a/tools/metadata/testdata/emptyInputFile.txt b/tools/metadata/testdata/emptyInputFile.txt
deleted file mode 100644
index 8b13789..0000000
--- a/tools/metadata/testdata/emptyInputFile.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/tools/metadata/testdata/expectedCodeMetadataOutput.txt b/tools/metadata/testdata/expectedCodeMetadataOutput.txt
deleted file mode 100644
index 755cf40..0000000
--- a/tools/metadata/testdata/expectedCodeMetadataOutput.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
- 
-bar
-Android.bp12346"b.java
- 
-foo
-Android.bp12345"a.java
\ No newline at end of file
diff --git a/tools/metadata/testdata/expectedOutputFile.txt b/tools/metadata/testdata/expectedOutputFile.txt
deleted file mode 100644
index b0d382f..0000000
--- a/tools/metadata/testdata/expectedOutputFile.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Android.bp12346
-.
-java-test-module-name-six
-Aqwerty.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-two
-Android.bp12345
-.
-java-test-module-name-two
-Asdfghj.bp12345
-.
-java-test-module-name-two
-Azxcvbn.bp12345
\ No newline at end of file
diff --git a/tools/metadata/testdata/file1.txt b/tools/metadata/testdata/file1.txt
deleted file mode 100644
index 81beed0..0000000
--- a/tools/metadata/testdata/file1.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-two
-Android.bp12345
-.
-java-test-module-name-two
-Asdfghj.bp12345
-.
-java-test-module-name-two
-Azxcvbn.bp12345
diff --git a/tools/metadata/testdata/file2.txt b/tools/metadata/testdata/file2.txt
deleted file mode 100644
index 32a753f..0000000
--- a/tools/metadata/testdata/file2.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Android.bp12346
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Aqwerty.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
diff --git a/tools/metadata/testdata/file3.txt b/tools/metadata/testdata/file3.txt
deleted file mode 100644
index 81beed0..0000000
--- a/tools/metadata/testdata/file3.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-two
-Android.bp12345
-.
-java-test-module-name-two
-Asdfghj.bp12345
-.
-java-test-module-name-two
-Azxcvbn.bp12345
diff --git a/tools/metadata/testdata/file4.txt b/tools/metadata/testdata/file4.txt
deleted file mode 100644
index 6a75900..0000000
--- a/tools/metadata/testdata/file4.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Android.bp12346
-.
-java-test-module-name-one
-Android.bp12346
-.
-java-test-module-name-six
-Aqwerty.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
diff --git a/tools/metadata/testdata/file5.txt b/tools/metadata/testdata/file5.txt
deleted file mode 100644
index d8de064..0000000
--- a/tools/metadata/testdata/file5.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
- 
-foo
-Android.bp12345"a.java
diff --git a/tools/metadata/testdata/file6.txt b/tools/metadata/testdata/file6.txt
deleted file mode 100644
index 9c7cdcd..0000000
--- a/tools/metadata/testdata/file6.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
- 
-bar
-Android.bp12346"b.java
diff --git a/tools/metadata/testdata/file7.txt b/tools/metadata/testdata/file7.txt
deleted file mode 100644
index d8de064..0000000
--- a/tools/metadata/testdata/file7.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
- 
-foo
-Android.bp12345"a.java
diff --git a/tools/metadata/testdata/file8.txt b/tools/metadata/testdata/file8.txt
deleted file mode 100644
index a931690..0000000
--- a/tools/metadata/testdata/file8.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-
- 
-foo
-Android.gp12346"a.java
diff --git a/tools/metadata/testdata/generatedCodeMetadataOutput.txt b/tools/metadata/testdata/generatedCodeMetadataOutput.txt
deleted file mode 100644
index 755cf40..0000000
--- a/tools/metadata/testdata/generatedCodeMetadataOutput.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
- 
-bar
-Android.bp12346"b.java
- 
-foo
-Android.bp12345"a.java
\ No newline at end of file
diff --git a/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt b/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt
deleted file mode 100644
index 755cf40..0000000
--- a/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
- 
-bar
-Android.bp12346"b.java
- 
-foo
-Android.bp12345"a.java
\ No newline at end of file
diff --git a/tools/metadata/testdata/generatedEmptyOutputFile.txt b/tools/metadata/testdata/generatedEmptyOutputFile.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/metadata/testdata/generatedEmptyOutputFile.txt
+++ /dev/null
diff --git a/tools/metadata/testdata/generatedOutputFile.txt b/tools/metadata/testdata/generatedOutputFile.txt
deleted file mode 100644
index b0d382f..0000000
--- a/tools/metadata/testdata/generatedOutputFile.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Android.bp12346
-.
-java-test-module-name-six
-Aqwerty.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-two
-Android.bp12345
-.
-java-test-module-name-two
-Asdfghj.bp12345
-.
-java-test-module-name-two
-Azxcvbn.bp12345
\ No newline at end of file
diff --git a/tools/metadata/testdata/inputCodeMetadata.txt b/tools/metadata/testdata/inputCodeMetadata.txt
deleted file mode 100644
index 7a81b7d..0000000
--- a/tools/metadata/testdata/inputCodeMetadata.txt
+++ /dev/null
@@ -1 +0,0 @@
-file5.txt file6.txt
\ No newline at end of file
diff --git a/tools/metadata/testdata/inputCodeMetadataNegative.txt b/tools/metadata/testdata/inputCodeMetadataNegative.txt
deleted file mode 100644
index 26668e4..0000000
--- a/tools/metadata/testdata/inputCodeMetadataNegative.txt
+++ /dev/null
@@ -1 +0,0 @@
-file7.txt file8.txt
\ No newline at end of file
diff --git a/tools/metadata/testdata/inputFiles.txt b/tools/metadata/testdata/inputFiles.txt
deleted file mode 100644
index e44bc94..0000000
--- a/tools/metadata/testdata/inputFiles.txt
+++ /dev/null
@@ -1 +0,0 @@
-file1.txt file2.txt
\ No newline at end of file
diff --git a/tools/metadata/testdata/inputFilesNegativeCase.txt b/tools/metadata/testdata/inputFilesNegativeCase.txt
deleted file mode 100644
index a37aa3f..0000000
--- a/tools/metadata/testdata/inputFilesNegativeCase.txt
+++ /dev/null
@@ -1 +0,0 @@
-file3.txt file4.txt
\ No newline at end of file
diff --git a/tools/metadata/testdata/metadata_test.go b/tools/metadata/testdata/metadata_test.go
deleted file mode 100644
index 314add3..0000000
--- a/tools/metadata/testdata/metadata_test.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os/exec"
-	"strings"
-	"testing"
-)
-
-func TestMetadata(t *testing.T) {
-	cmd := exec.Command(
-		"metadata", "-rule", "test_spec", "-inputFile", "./inputFiles.txt", "-outputFile",
-		"./generatedOutputFile.txt",
-	)
-	stderr, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err)
-	}
-
-	// Read the contents of the expected output file
-	expectedOutput, err := ioutil.ReadFile("./expectedOutputFile.txt")
-	if err != nil {
-		t.Fatalf("Error reading expected output file: %s", err)
-	}
-
-	// Read the contents of the generated output file
-	generatedOutput, err := ioutil.ReadFile("./generatedOutputFile.txt")
-	if err != nil {
-		t.Fatalf("Error reading generated output file: %s", err)
-	}
-
-	fmt.Println()
-
-	// Compare the contents
-	if string(expectedOutput) != string(generatedOutput) {
-		t.Errorf("Generated file contents do not match the expected output")
-	}
-}
-
-func TestMetadataNegativeCase(t *testing.T) {
-	cmd := exec.Command(
-		"metadata", "-rule", "test_spec", "-inputFile", "./inputFilesNegativeCase.txt", "-outputFile",
-		"./generatedOutputFileNegativeCase.txt",
-	)
-	stderr, err := cmd.CombinedOutput()
-	if err == nil {
-		t.Fatalf(
-			"Expected an error, but the metadata command executed successfully. Output: %s",
-			stderr,
-		)
-	}
-
-	expectedError := "Conflicting trendy team IDs found for java-test-module" +
-		"-name-one at:\nAndroid.bp with teamId: 12346," +
-		"\nAndroid.bp with teamId: 12345"
-	if !strings.Contains(
-		strings.TrimSpace(string(stderr)), strings.TrimSpace(expectedError),
-	) {
-		t.Errorf(
-			"Unexpected error message. Expected to contain: %s, Got: %s",
-			expectedError, stderr,
-		)
-	}
-}
-
-func TestEmptyInputFile(t *testing.T) {
-	cmd := exec.Command(
-		"metadata", "-rule", "test_spec", "-inputFile", "./emptyInputFile.txt", "-outputFile",
-		"./generatedEmptyOutputFile.txt",
-	)
-	stderr, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err)
-	}
-
-	// Read the contents of the generated output file
-	generatedOutput, err := ioutil.ReadFile("./generatedEmptyOutputFile.txt")
-	if err != nil {
-		t.Fatalf("Error reading generated output file: %s", err)
-	}
-
-	fmt.Println()
-
-	// Compare the contents
-	if string(generatedOutput) != "\n" {
-		t.Errorf("Generated file contents do not match the expected output")
-	}
-}
-
-func TestCodeMetadata(t *testing.T) {
-	cmd := exec.Command(
-		"metadata", "-rule", "code_metadata", "-inputFile", "./inputCodeMetadata.txt", "-outputFile",
-		"./generatedCodeMetadataOutputFile.txt",
-	)
-	stderr, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err)
-	}
-
-	// Read the contents of the expected output file
-	expectedOutput, err := ioutil.ReadFile("./expectedCodeMetadataOutput.txt")
-	if err != nil {
-		t.Fatalf("Error reading expected output file: %s", err)
-	}
-
-	// Read the contents of the generated output file
-	generatedOutput, err := ioutil.ReadFile("./generatedCodeMetadataOutputFile.txt")
-	if err != nil {
-		t.Fatalf("Error reading generated output file: %s", err)
-	}
-
-	fmt.Println()
-
-	// Compare the contents
-	if string(expectedOutput) != string(generatedOutput) {
-		t.Errorf("Generated file contents do not match the expected output")
-	}
-}
diff --git a/tools/metadata/testdata/outputFile.txt b/tools/metadata/testdata/outputFile.txt
deleted file mode 100644
index b0d382f..0000000
--- a/tools/metadata/testdata/outputFile.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-
-.
-java-test-module-name-one
-Android.bp12345
-.
-java-test-module-name-six
-Android.bp12346
-.
-java-test-module-name-six
-Aqwerty.bp12346
-.
-java-test-module-name-six
-Apoiuyt.bp12346
-.
-java-test-module-name-two
-Android.bp12345
-.
-java-test-module-name-two
-Asdfghj.bp12345
-.
-java-test-module-name-two
-Azxcvbn.bp12345
\ No newline at end of file
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 8c71044..e371b23 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -637,6 +637,8 @@
     ],
     data: [
         "testdata/**/*",
+    ],
+    device_common_data: [
         ":com.android.apex.compressed.v1",
         ":com.android.apex.vendor.foo.with_vintf",
     ],